import { relations } from "drizzle-orm";
import {
	index,
	integer,
	primaryKey,
	sqliteTable,
	text,
	uniqueIndex,
} from "drizzle-orm/sqlite-core";

/**
 * Normalized schema for AMQ source data (imported from JSON).
 *
 * Source Zod types (src/lib/types/amq):
 * - AmqAnimeSchema (anime + names + genres + tags + songLinks)
 * - AmqSongSchema (song + category + artist/group ids for roles)
 * - AmqArtistSchema (artist + memberships + altNames)
 * - AmqGroupSchema (group + members + altNames)
 *
 * Notes:
 * - We keep original AMQ integer identifiers where present (annId, songId, annSongId, songArtistId, songGroupId, etc.)
 * - Arrays (genres, tags, names, songLinks) are normalized to separate tables.
 * - Artists and groups are stored in separate tables: `artists` and `groups`.
 * - Membership and alt-names are normalized to join tables.
 * - Anime-song linkage from `songLinks` is stored in `anime_song_links` (unique per anime+song).
 */

// ----------------------
// Core tables
// ----------------------

export const animeTable = sqliteTable(
	"anime",
	{
		/** AMQ anime ID */
		annId: integer("ann_id").notNull().primaryKey(),

		// External IDs from the source
		aniListId: integer("anilist_id"),
		malId: integer("mal_id").notNull(),
		kitsuId: integer("kitsu_id"),

		// Category object (name + number that can be number|string|null in source)
		categoryName: text("category_name").notNull(),
		categoryNumber: text("category_number"),

		// Names
		mainName: text("main_name").notNull(),
		mainNameEn: text("main_name_en"),
		mainNameJa: text("main_name_ja"),

		// Season/year
		year: integer("year").notNull(),
		seasonId: integer("season_id").notNull(), // 0..3 from Season enum

		// Counts
		opCount: integer("op_count").notNull(),
		edCount: integer("ed_count").notNull(),
		insertCount: integer("insert_count").notNull(),
	},
	(t) => ({
		aniListIndex: index("anime_anilist_id_uq").on(t.aniListId),
		malIndex: index("anime_mal_id_uq").on(t.malId),
		kitsuIndex: index("anime_kitsu_id_uq").on(t.kitsuId),
	}),
);

export const songsTable = sqliteTable(
	"songs",
	{
		/** AMQ annSongId (ANN song id) */
		annSongId: integer("ann_song_id").notNull().primaryKey(),

		/** AMQ songId */
		songId: integer("song_id").notNull(),

		name: text("name").notNull(),

		/**
		 * AmqSongCategory enum from the source:
		 * none(0), instrumental(1), chanting(2), character(3), standard(4)
		 */
		category: integer("category").notNull(),
	},
	(t) => ({
		songIdIndex: index("songs_song_id_idx").on(t.songId),
	}),
);

export const artistsTable = sqliteTable(
	"artists",
	{
		/** AMQ songArtistId */
		songArtistId: integer("song_artist_id").notNull().primaryKey(),
		name: text("name").notNull(),
	},
	(t) => ({
		nameIndex: index("artists_name_idx").on(t.name),
	}),
);

export const groupsTable = sqliteTable(
	"groups",
	{
		/** AMQ songGroupId */
		songGroupId: integer("song_group_id").notNull().primaryKey(),
		name: text("name").notNull(),
	},
	(t) => ({
		nameIndex: index("groups_name_idx").on(t.name),
	}),
);

/**
 * Artist -> Groups membership (from AmqArtistSchema.inGroups)
 */
export const artistGroupsTable = sqliteTable(
	"artist_groups",
	{
		songArtistId: integer("song_artist_id")
			.notNull()
			.references(() => artistsTable.songArtistId, { onDelete: "cascade" }),
		songGroupId: integer("song_group_id")
			.notNull()
			.references(() => groupsTable.songGroupId, { onDelete: "cascade" }),
	},
	(t) => ({
		pk: primaryKey({
			name: "artist_groups_pk",
			columns: [t.songArtistId, t.songGroupId],
		}),
		artistIndex: index("artist_groups_artist_id_idx").on(t.songArtistId),
		groupIndex: index("artist_groups_group_id_idx").on(t.songGroupId),
	}),
);

/**
 * Group -> Artist members (from AmqGroupSchema.artistMembers)
 */
export const groupArtistMembersTable = sqliteTable(
	"group_artist_members",
	{
		songGroupId: integer("song_group_id")
			.notNull()
			.references(() => groupsTable.songGroupId, { onDelete: "cascade" }),
		songArtistId: integer("song_artist_id")
			.notNull()
			.references(() => artistsTable.songArtistId, { onDelete: "cascade" }),
	},
	(t) => ({
		pk: primaryKey({
			name: "group_artist_members_pk",
			columns: [t.songGroupId, t.songArtistId],
		}),
		groupIndex: index("group_artist_members_group_id_idx").on(t.songGroupId),
		artistIndex: index("group_artist_members_artist_id_idx").on(t.songArtistId),
	}),
);

/**
 * Group -> Group members (from AmqGroupSchema.groupMembers)
 */
export const groupGroupMembersTable = sqliteTable(
	"group_group_members",
	{
		songGroupId: integer("song_group_id")
			.notNull()
			.references(() => groupsTable.songGroupId, { onDelete: "cascade" }),
		memberSongGroupId: integer("member_song_group_id")
			.notNull()
			.references(() => groupsTable.songGroupId, { onDelete: "cascade" }),
	},
	(t) => ({
		pk: primaryKey({
			name: "group_group_members_pk",
			columns: [t.songGroupId, t.memberSongGroupId],
		}),
		groupIndex: index("group_group_members_group_id_idx").on(t.songGroupId),
		memberIndex: index("group_group_members_member_group_id_idx").on(
			t.memberSongGroupId,
		),
	}),
);

/**
 * Alternative names for artists and groups (both schemas use:
 * { songGroupId, name }
 *
 * Interpreted as: "the alias `name` used in/for group `songGroupId`".
 */
export const artistAltNamesTable = sqliteTable(
	"artist_alt_names",
	{
		id: integer("id").notNull().primaryKey({ autoIncrement: true }),
		/**
		 * The "owning" artist.
		 */
		songArtistId: integer("song_artist_id")
			.notNull()
			.references(() => artistsTable.songArtistId, { onDelete: "cascade" }),

		/**
		 * The alternate-name entry is keyed by an artist id in the source:
		 * { songArtistId, name }
		 *
		 * Interpreted as: artist `songArtistId` is also known as `name`.
		 */
		altSongArtistId: integer("alt_song_artist_id")
			.notNull()
			.references(() => artistsTable.songArtistId, { onDelete: "cascade" }),

		name: text("name").notNull(),
	},
	(t) => ({
		artistIndex: index("artist_alt_names_artist_id_idx").on(t.songArtistId),
		altArtistIndex: index("artist_alt_names_alt_artist_id_idx").on(
			t.altSongArtistId,
		),
		nameIndex: index("artist_alt_names_name_idx").on(t.name),
		uniquePerArtistAltArtistName: uniqueIndex(
			"artist_alt_names_artist_alt_artist_name_uq",
		).on(t.songArtistId, t.altSongArtistId, t.name),
	}),
);

export const groupAltNamesTable = sqliteTable(
	"group_alt_names",
	{
		id: integer("id").notNull().primaryKey({ autoIncrement: true }),
		songGroupId: integer("song_group_id")
			.notNull()
			.references(() => groupsTable.songGroupId, { onDelete: "cascade" }),
		/**
		 * In the source shape, this is also called `songGroupId` inside the altName object.
		 * Here we store it as the "context group" the alias is associated with.
		 */
		contextSongGroupId: integer("context_song_group_id")
			.notNull()
			.references(() => groupsTable.songGroupId, { onDelete: "cascade" }),
		name: text("name").notNull(),
	},
	(t) => ({
		groupIndex: index("group_alt_names_group_id_idx").on(t.songGroupId),
		contextGroupIndex: index("group_alt_names_context_group_id_idx").on(
			t.contextSongGroupId,
		),
		nameIndex: index("group_alt_names_name_idx").on(t.name),
		uniquePerGroupContextName: uniqueIndex(
			"group_alt_names_group_context_name_uq",
		).on(t.songGroupId, t.contextSongGroupId, t.name),
	}),
);

// ----------------------
// Anime: names / genres / tags
// ----------------------

export const animeNamesTable = sqliteTable(
	"anime_names",
	{
		id: integer("id").notNull().primaryKey({ autoIncrement: true }),
		annId: integer("ann_id")
			.notNull()
			.references(() => animeTable.annId, { onDelete: "cascade" }),

		/** "EN" | "JA" per source */
		language: text("language").notNull(),

		name: text("name").notNull(),
	},
	(t) => ({
		animeIndex: index("anime_names_ann_id_idx").on(t.annId),
		nameIndex: index("anime_names_name_idx").on(t.name),
		uniquePerAnime: uniqueIndex("anime_names_ann_lang_name_uq").on(
			t.annId,
			t.language,
			t.name,
		),
	}),
);

export const genresTable = sqliteTable(
	"genres",
	{
		/** Primary key is the genre string itself */
		name: text("name").notNull().primaryKey(),
	},
	(t) => ({
		nameIndex: index("genres_name_idx").on(t.name),
	}),
);

export const animeGenresTable = sqliteTable(
	"anime_genres",
	{
		annId: integer("ann_id")
			.notNull()
			.references(() => animeTable.annId, { onDelete: "cascade" }),
		genreName: text("genre_name")
			.notNull()
			.references(() => genresTable.name, { onDelete: "cascade" }),
	},
	(t) => ({
		pk: primaryKey({
			name: "anime_genres_pk",
			columns: [t.annId, t.genreName],
		}),
		animeIndex: index("anime_genres_ann_id_idx").on(t.annId),
		genreIndex: index("anime_genres_genre_name_idx").on(t.genreName),
	}),
);

export const tagsTable = sqliteTable(
	"tags",
	{
		/** Primary key is the tag string itself */
		name: text("name").notNull().primaryKey(),
	},
	(t) => ({
		nameIndex: index("tags_name_idx").on(t.name),
	}),
);

export const animeTagsTable = sqliteTable(
	"anime_tags",
	{
		annId: integer("ann_id")
			.notNull()
			.references(() => animeTable.annId, { onDelete: "cascade" }),
		tagName: text("tag_name")
			.notNull()
			.references(() => tagsTable.name, { onDelete: "cascade" }),
	},
	(t) => ({
		pk: primaryKey({
			name: "anime_tags_pk",
			columns: [t.annId, t.tagName],
		}),
		animeIndex: index("anime_tags_ann_id_idx").on(t.annId),
		tagIndex: index("anime_tags_tag_name_idx").on(t.tagName),
	}),
);

// ----------------------
// Anime <-> Songs links (from AmqSongLink)
// ----------------------

export const animeSongLinksTable = sqliteTable(
	"anime_song_links",
	{
		annId: integer("ann_id")
			.notNull()
			.references(() => animeTable.annId, { onDelete: "cascade" }),

		annSongId: integer("ann_song_id")
			.notNull()
			.references(() => songsTable.annSongId, { onDelete: "cascade" }),

		/** 1(OP) | 2(ED) | 3(INS) per SongLinkType */
		type: integer("type").notNull(),

		/** link number within type (1-based in source) */
		number: integer("number").notNull(),

		/** boolean ints 0/1 in source */
		uploaded: integer("uploaded").notNull(),
		rebroadcast: integer("rebroadcast").notNull(),
		dub: integer("dub").notNull(),
	},
	(t) => ({
		pk: primaryKey({ name: "anime_songs_pk", columns: [t.annId, t.annSongId] }),
		annIdIndex: index("anime_songs_ann_id_idx").on(t.annId),
		songIdIndex: index("anime_songs_song_id_idx").on(t.annSongId),
	}),
);

// ----------------------
// Relations (optional but helpful for Drizzle queries)
// ----------------------

export const animeRelations = relations(animeTable, ({ many }) => ({
	names: many(animeNamesTable),
	genres: many(animeGenresTable),
	tags: many(animeTagsTable),
	songLinks: many(animeSongLinksTable),
}));

export const songRelations = relations(songsTable, ({ many }) => ({
	animeLinks: many(animeSongLinksTable),
}));

export const artistRelations = relations(artistsTable, ({ many }) => ({
	inGroups: many(artistGroupsTable),
	altNames: many(artistAltNamesTable),
	groupMemberships: many(groupArtistMembersTable),
}));

export const groupRelations = relations(groupsTable, ({ many }) => ({
	artists: many(artistGroupsTable),
	artistMembers: many(groupArtistMembersTable),
	groupMembers: many(groupGroupMembersTable),
	altNames: many(groupAltNamesTable),
}));

export const animeNamesRelations = relations(animeNamesTable, ({ one }) => ({
	anime: one(animeTable, {
		fields: [animeNamesTable.annId],
		references: [animeTable.annId],
	}),
}));

export const animeGenresRelations = relations(animeGenresTable, ({ one }) => ({
	anime: one(animeTable, {
		fields: [animeGenresTable.annId],
		references: [animeTable.annId],
	}),
	genre: one(genresTable, {
		fields: [animeGenresTable.genreName],
		references: [genresTable.name],
	}),
}));

export const animeTagsRelations = relations(animeTagsTable, ({ one }) => ({
	anime: one(animeTable, {
		fields: [animeTagsTable.annId],
		references: [animeTable.annId],
	}),
	tag: one(tagsTable, {
		fields: [animeTagsTable.tagName],
		references: [tagsTable.name],
	}),
}));

export const animeSongLinksRelations = relations(
	animeSongLinksTable,
	({ one }) => ({
		anime: one(animeTable, {
			fields: [animeSongLinksTable.annId],
			references: [animeTable.annId],
		}),
		song: one(songsTable, {
			fields: [animeSongLinksTable.annSongId],
			references: [songsTable.annSongId],
		}),
	}),
);
