import { TRPCError } from "@trpc/server";
import { eq } from "drizzle-orm";
import { database } from "@/db";
import { libraryFolders } from "@/db/schema/libraries";
import { t } from "@/lib/trpc";
import {
	CreateLibraryInputSchema,
	CreateLibraryOutputSchema,
	DeleteLibraryInputSchema,
	DeleteLibraryOutputSchema,
	GetLibraryInputSchema,
	GetLibraryOutputSchema,
	ListLibrariesOutputSchema,
	ScanLibraryInputSchema,
	ScanLibraryOutputSchema,
	UpdateLibraryInputSchema,
	UpdateLibraryOutputSchema,
} from "@/models/trpc/dashboard/libraries";
import { LibraryScannerService } from "@/services/libraryScanner/LibraryScanner";
import { adminProcedure, dashboardProcedure } from "@/trpc/procedures";

export const librariesRouter = t.router({
	list: dashboardProcedure.output(ListLibrariesOutputSchema).query(async () => {
		const libraries = await database.select().from(libraryFolders);
		return libraries;
	}),

	getById: dashboardProcedure
		.input(GetLibraryInputSchema)
		.output(GetLibraryOutputSchema)
		.query(async ({ input }) => {
			const [library] = await database
				.select()
				.from(libraryFolders)
				.where(eq(libraryFolders.id, input.id))
				.limit(1);

			if (!library) {
				throw new TRPCError({
					code: "NOT_FOUND",
					message: `Library with ID ${input.id} not found.`,
				});
			}

			return library;
		}),

	create: adminProcedure
		.input(CreateLibraryInputSchema)
		.output(CreateLibraryOutputSchema)
		.mutation(async ({ input }) => {
			const existingByName = await database
				.select()
				.from(libraryFolders)
				.where(eq(libraryFolders.name, input.name))
				.limit(1);

			if (existingByName.length > 0) {
				throw new TRPCError({
					code: "CONFLICT",
					message: `A library with the name "${input.name}" already exists.`,
				});
			}

			const existingByPath = await database
				.select()
				.from(libraryFolders)
				.where(eq(libraryFolders.path, input.path))
				.limit(1);

			if (existingByPath.length > 0) {
				throw new TRPCError({
					code: "CONFLICT",
					message: `A library with the path "${input.path}" already exists.`,
				});
			}

			const [newLibrary] = await database
				.insert(libraryFolders)
				.values({
					name: input.name,
					type: input.type,
					path: input.path,
					primaryLanguage: input.primaryLanguage,
					fallbackLanguage: input.fallbackLanguage,
				})
				.returning();

			return newLibrary;
		}),

	update: adminProcedure
		.input(UpdateLibraryInputSchema)
		.output(UpdateLibraryOutputSchema)
		.mutation(async ({ input }) => {
			const [existingLibrary] = await database
				.select()
				.from(libraryFolders)
				.where(eq(libraryFolders.id, input.id))
				.limit(1);

			if (!existingLibrary) {
				throw new TRPCError({
					code: "NOT_FOUND",
					message: `Library with ID ${input.id} not found.`,
				});
			}

			if (input.name) {
				const [existingByName] = await database
					.select()
					.from(libraryFolders)
					.where(eq(libraryFolders.name, input.name))
					.limit(1);

				if (existingByName && existingByName.id !== input.id) {
					throw new TRPCError({
						code: "CONFLICT",
						message: `A library with the name "${input.name}" already exists.`,
					});
				}
			}

			if (input.path) {
				const [existingByPath] = await database
					.select()
					.from(libraryFolders)
					.where(eq(libraryFolders.path, input.path))
					.limit(1);

				if (existingByPath && existingByPath.id !== input.id) {
					throw new TRPCError({
						code: "CONFLICT",
						message: `A library with the path "${input.path}" already exists.`,
					});
				}
			}

			const updateData: Record<string, unknown> = {};
			if (input.name !== undefined) updateData.name = input.name;
			if (input.path !== undefined) updateData.path = input.path;
			if (input.primaryLanguage !== undefined)
				updateData.primaryLanguage = input.primaryLanguage;
			if (input.fallbackLanguage !== undefined)
				updateData.fallbackLanguage = input.fallbackLanguage;

			const [updatedLibrary] = await database
				.update(libraryFolders)
				.set(updateData)
				.where(eq(libraryFolders.id, input.id))
				.returning();

			return updatedLibrary;
		}),

	delete: adminProcedure
		.input(DeleteLibraryInputSchema)
		.output(DeleteLibraryOutputSchema)
		.mutation(async ({ input }) => {
			const [existingLibrary] = await database
				.select()
				.from(libraryFolders)
				.where(eq(libraryFolders.id, input.id))
				.limit(1);

			if (!existingLibrary) {
				throw new TRPCError({
					code: "NOT_FOUND",
					message: `Library with ID ${input.id} not found.`,
				});
			}

			await database
				.delete(libraryFolders)
				.where(eq(libraryFolders.id, input.id));

			return {
				success: true,
				message: `Library "${existingLibrary.name}" has been deleted successfully.`,
			};
		}),

	scan: adminProcedure
		.input(ScanLibraryInputSchema)
		.output(ScanLibraryOutputSchema)
		.mutation(async ({ ctx, input }) => {
			const [library] = await database
				.select()
				.from(libraryFolders)
				.where(eq(libraryFolders.id, input.id))
				.limit(1);

			if (!library) {
				throw new TRPCError({
					code: "NOT_FOUND",
					message: `Library with ID ${input.id} not found.`,
				});
			}

			if (!ctx.serviceContainer) {
				throw new TRPCError({
					code: "INTERNAL_SERVER_ERROR",
					message: "Service container is not available.",
				});
			}

			const scannerService = ctx.serviceContainer.get(LibraryScannerService);

			if (!scannerService.isRunning) {
				throw new TRPCError({
					code: "PRECONDITION_FAILED",
					message: "Library scanner service is not running.",
				});
			}

			try {
				await scannerService.scanLibraryById(input.id);

				return {
					success: true,
					message: `Library "${library.name}" has been scanned successfully.`,
				};
			} catch (error) {
				throw new TRPCError({
					code: "INTERNAL_SERVER_ERROR",
					message: `Failed to scan library: ${error instanceof Error ? error.message : "Unknown error"}`,
				});
			}
		}),
});
