import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import { t } from "@/lib/trpc";
import { Directory, OnboardConfigurationData, Path } from "@/models";
import { OnboardService } from "@/services/onboard";
import { publicProcedure } from "@/trpc/procedures";
import { getDrivesWMIC } from "@/utils/disks";
import { onboardMiddleware } from "../middleware/onboard";

const onboardProcedure = t.procedure.use(onboardMiddleware);

export const onboardRouter = t.router({
	// This endpoint can be called to check if onboarding is needed
	needsOnboarding: publicProcedure.query(({ ctx }) => {
		if (!ctx.serviceContainer) {
			return { needsOnboarding: true }; // Default to needing onboarding if service container unavailable
		}
		const onboardService = ctx.serviceContainer.get(OnboardService);
		return { needsOnboarding: onboardService.getNeedsOnboarding() };
	}),

	getFolderContents: onboardProcedure
		.input(Path)
		.output(Directory.array())
		.mutation(async ({ input }) => {
			try {
				if (input.path.length === 0) {
					if (os.platform() === "linux") {
						try {
							const drives = fs.readdirSync("/", { withFileTypes: true });
							return drives
								.filter((v) => v.isDirectory())
								.map(
									(v): Directory => ({ name: `/${v.name}`, type: "FOLDER" }),
								);
						} catch (error) {
							// If we can't read root, return empty array
							console.error("Failed to read root directory:", error);
							return [];
						}
					}
					try {
						const drives = getDrivesWMIC();
						return drives.map((value): Directory => {
							return {
								name: `${value}/`,
								type: "FOLDER",
							};
						});
					} catch (error) {
						// If we can't get drives, return empty array
						console.error("Failed to get drives:", error);
						return [];
					}
				}

				// On Linux, ensure path starts with / for absolute paths
				const inputPath =
					os.platform() === "linux"
						? path.join("/", ...input.path)
						: path.join(...input.path);

				// Validate path before attempting to read
				if (!fs.existsSync(inputPath)) {
					throw new Error(`Path does not exist: ${inputPath}`);
				}

				const stats = fs.statSync(inputPath);
				if (!stats.isDirectory()) {
					throw new Error(`Path is not a directory: ${inputPath}`);
				}

				const dirs = fs.readdirSync(inputPath, {
					withFileTypes: true,
				});

				return dirs
					.filter((v) => v.isDirectory())
					.map((value): Directory => ({ name: value.name, type: "FOLDER" }));
			} catch (error) {
				// Handle specific error types
				if (error instanceof Error) {
					const nodeError = error as NodeJS.ErrnoException;

					// Access denied / Permission denied
					if (nodeError.code === "EACCES" || nodeError.code === "EPERM") {
						throw new Error(
							`Access denied: You don't have permission to access this directory`,
						);
					}

					// Path not found
					if (nodeError.code === "ENOENT") {
						throw new Error(
							"Directory not found: The specified path does not exist",
						);
					}

					// Path too long
					if (nodeError.code === "ENAMETOOLONG") {
						throw new Error("Path is too long");
					}

					// Not a directory
					if (nodeError.code === "ENOTDIR") {
						throw new Error(
							"Not a directory: The path is not a valid directory",
						);
					}

					// Too many symbolic links
					if (nodeError.code === "ELOOP") {
						throw new Error(
							"Too many symbolic links: Unable to resolve the path",
						);
					}

					// Input/output error
					if (nodeError.code === "EIO") {
						throw new Error("I/O error: Unable to read from the filesystem");
					}

					// Re-throw the original error message if it's already formatted
					throw error;
				}

				// Unknown error
				throw new Error(
					`Failed to read directory: ${error instanceof Error ? error.message : "Unknown error"}`,
				);
			}
		}),

	completeOnboarding: onboardProcedure
		.input(OnboardConfigurationData)
		.mutation(async ({ ctx, input }) => {
			await ctx.onboardService.completeOnboarding(input);
			return { success: true, completed: true };
		}),
});
