import { TRPCError } from "@trpc/server";
import type { UsersId } from "~/schemas/public/Users";
import type { NewUsersStorage } from "~/schemas/public/UsersStorage";
import { db, type Querier } from "~/server/db";
import { fetchFiles, retriveFilesSafely } from "../services/storage.service";

export const getMultipleWithRelations = async ({
	db,
	ids,
}: {
	db: Querier;
	ids: string[];
}) => {
	const files = await fetchFiles();
	const userStorage = await db
		.selectFrom("users_storage")
		.selectAll("users_storage")
		.innerJoin("users", "users.id", "users_storage.userId")
		.select("users.username")
		.where(
			"storageId",
			"in",
			files.map((f) => f.id),
		)

		.execute();

	return ids
		.map((id) => {
			const file = files.find((f) => f.id === id);
			if (!file) return null;
			const users = userStorage
				.filter((us) => us.storageId === id)
				.map((us) => ({
					id: us.userId,
					username: us.username,
				}));
			return {
				...file,
				users,
			};
		})
		.filter((f) => f !== null);
};

export const retrieveUserFiles = async ({ userId }: { userId: UsersId }) => {
	try {
		//all files linked to the user
		const files = await db
			.selectFrom("users_storage")
			.select(["storageId", "from_admin"])
			.where("userId", "=", userId)
			.execute();
		const { available, unavailable } = await retriveFilesSafely(
			files.map((f) => f.storageId),
		);

		//remove entries in users_storage for files that no longer exist in storage
		await db
			.deleteFrom("users_storage")
			.where("userId", "=", userId)
			.where("storageId", "in", unavailable)
			.execute();

		//merge file metadata with users_storage info
		const userFiles = files
			.map((uf) => {
				const file = available.find((f) => f.id === uf.storageId);
				if (!file) return null;
				return {
					...file,
					from_admin: uf.from_admin,
				};
			})
			.filter((f) => f !== null);

		return userFiles;
	} catch (e) {
		throw new TRPCError({
			code: "INTERNAL_SERVER_ERROR",
			message: `Error while getting user storage: ${(e as Error).message}`,
		});
	}
};

export const getAllWithFromAdmin = async () => {
	try {
		const files = await db
			.selectFrom("users_storage")
			.select(["storageId", "from_admin"])
			.execute();

		const allFiles = await fetchFiles();
		const allFilesWithFromAdmin = allFiles.map((file) => {
			const userStorageEntry = files.find((f) => f.storageId === file.id);
			return {
				...file,
				from_admin: userStorageEntry ? userStorageEntry.from_admin : true,
			};
		});

		return allFilesWithFromAdmin;
	} catch (e) {
		throw new TRPCError({
			code: "INTERNAL_SERVER_ERROR",
			message: `Error while getting user storage: ${(e as Error).message}`,
		});
	}
};

export const addUserStorage = async (data: NewUsersStorage) => {
	try {
		return await db
			.insertInto("users_storage")
			.values(data)
			.onConflict((oc) => oc.columns(["userId", "storageId"]).doNothing())
			.returning("user_storage_id")
			.executeTakeFirst();
	} catch (e) {
		throw new TRPCError({
			code: "INTERNAL_SERVER_ERROR",
			message: `Error while adding user storage: ${(e as Error).message}`,
		});
	}
};

export const deleteUserStorageEntry = async ({
	userId,
	storageId,
}: {
	userId: UsersId;
	storageId: string;
}) => {
	try {
		await db
			.deleteFrom("users_storage")
			.where("userId", "=", userId)
			.where("storageId", "=", storageId)
			.execute();
		return "ok";
	} catch (e) {
		throw new TRPCError({
			code: "INTERNAL_SERVER_ERROR",
			message: `Error while deleting user storage: ${(e as Error).message}`,
		});
	}
};

export const purgeFileFromUserStorages = async ({
	storageId,
}: {
	storageId: string;
}) => {
	try {
		await db
			.deleteFrom("users_storage")
			.where("storageId", "=", storageId)
			.execute();
		return "ok";
	} catch (e) {
		throw new TRPCError({
			code: "INTERNAL_SERVER_ERROR",
			message: `Error while deleting user storage: ${(e as Error).message}`,
		});
	}
};
