import { db } from "@pkg/db";
import { UserRepository } from "../data/repository";
import {
	paginatedUserModel,
	type CreateUserPayloadModel,
	type PaginatedUserModel,
	type UpdateUserInfoInputModel,
	type UserModel,
} from "../data/entities";
import { UserRoleMap } from "$lib/core/enums";
import type { Result } from "@pkg/result";
import { ERROR_CODES } from "$lib/core/error.codes";
import type { PaginationModel } from "@pkg/logic/core/pagination.utils";
import { getError, Logger } from "$lib/core/logger";
import { auth } from "$lib/domains/auth/config/base";

export class UserController {
	private repo: UserRepository;

	constructor() {
		this.repo = new UserRepository(db);
	}

	async getUserById(id: string) {
		return await this.repo.getUserById(id);
	}

	async getUserBy(getBy: {
		username?: string;
		email?: string;
	}): Promise<Result<UserModel>> {
		return await this.repo.getUserBy(getBy);
	}

	async getAgentInfo(agentId: string, parentId: string) {
		const parent = await this.repo.getUserById(parentId);
		if (parent.error) {
			return { error: parent.error };
		}
		const child = await this.repo.getUserById(agentId);
		if (child.error) {
			return { error: child.error };
		}
		if (child.data?.parentId !== parent.data?.id) {
			return {
				error: {
					code: ERROR_CODES.PERMISSION_ERROR,
					message: "Unauthorized",
					userHint: "You are not authorized to view this user",
					detail: "Unauthorized to view user",
					actionable: false,
				},
			};
		}
		return child;
	}

	async getAgentsPaginatedInfo(
		requester: UserModel,
		pInfo: PaginationModel,
		fetchNext: boolean,
	): Promise<Result<PaginatedUserModel>> {
		let setNew = false;
		if (pInfo.totalPages <= 0) {
			const totalItems = await this.repo.getMyUsersCount(requester.id, {
				cursor: pInfo.cursor,
			});
			Logger.info(`${requester.username} has ${totalItems.data} users`);
			if (totalItems.error || !totalItems.data) {
				return { error: totalItems.error };
			}
			pInfo.totalItemCount = totalItems.data;
			pInfo.totalPages = Math.ceil(totalItems.data / pInfo.limit);
			pInfo.page = 1;
			setNew = true;
		}

		Logger.debug({
			reqId: requester.id,
			reqName: requester.username,
			...pInfo,
			fetchNext,
			message: "Fetching leads",
		});

		const { data, error } = await this.repo.getMyUsersInfo(requester.id, {
			fetchNext,
			asc: pInfo.asc,
			limit: pInfo.limit,
			cursor: pInfo.cursor,
		});
		if (error || !data) {
			return { error };
		}

		Logger.debug({
			reqId: requester.id,
			reqName: requester.username,
			message: `Fetched ${data.data.length} leads`,
		});

		let currPage = pInfo.page;
		if (!setNew) {
			currPage = fetchNext
				? Math.min(pInfo.page + 1, pInfo.totalPages)
				: Math.max(pInfo.page - 1, 1);
		}

		const outInfo = paginatedUserModel.safeParse({
			data: data.data,
			cursor: data.cursor,
			limit: pInfo.limit,
			asc: pInfo.asc,
			totalItemCount: pInfo.totalItemCount,
			totalPages: pInfo.totalPages,
			page: currPage,
		});
		if (!outInfo.success) {
			return {
				error: getError(
					{
						code: ERROR_CODES.VALIDATION_ERROR,
						message: "Something went wrong parsing the data",
						actionable: false,
						userHint: "Please try again after refreshing page",
						detail: "Please try again",
					},
					outInfo.error.errors[0],
				),
			};
		}
		Logger.debug({
			reqId: requester.id,
			reqName: requester.username,
			message: `Parsed ${outInfo.data.data.length} leads`,
		});
		return { data: outInfo.data };
	}

	async listChildUsers(parentId: string) {
		return await this.repo.listChildUsers(parentId);
	}

	async createUser(
		parentId: string,
		userInfo: CreateUserPayloadModel,
		reqHeaders: any,
	): Promise<Result<boolean>> {
		const found = await this.repo.doesUserExist(
			userInfo.email,
			userInfo.username,
		);
		if (found.data) {
			return {
				error: {
					code: ERROR_CODES.VALIDATION_ERROR,
					message: "User already exists",
					userHint: "A user with this username or email already exists",
					actionable: false,
					detail: "An agent with similar info(username || email) already exists",
				},
			};
		}

		try {
			const cOut = await auth.api.createUser({
				body: {
					name: userInfo.username,
					email: userInfo.email,
					password: userInfo.password,
					role: userInfo.role,
					data: {
						username: userInfo.username,
						parentId: parentId,
					},
				},
				headers: reqHeaders,
			});
			Logger.debug(cOut);
			Logger.debug(`A user has been created (${userInfo.username})`);

			await this.repo.setParentId(userInfo.username, parentId);

			return { data: true };
		} catch (err) {
			return {
				error: getError(
					{
						code: ERROR_CODES.DATABASE_ERROR,
						message: "Failed to create user",
						userHint: "Please contact support to solve the issue",
						actionable: false,
						detail: "An error is raised while creating user in the database",
					},
					err,
				),
			};
		}
	}

	async updateUsersInfo(parentId: string, userInfo: UpdateUserInfoInputModel) {
		const user = await this.repo.getUserById(parentId);
		if (user.error) {
			return { error: user.error };
		}
		if (user.data?.role !== UserRoleMap.ADMIN) {
			return {
				error: {
					code: ERROR_CODES.PERMISSION_ERROR,
					message: "Cannot update agent",
					userHint: "You must be a manager to change agent's info",
					actionable: false,
					detail: "Unauthorized to modify agent info",
				},
			};
		}
		return await this.repo.updateUsersInfo(parentId, userInfo);
	}

	async updateProfileInfo(reqId: string, userInfo: UpdateUserInfoInputModel) {
		const user = await this.repo.getUsersIdByEmail(userInfo.email);
		if (user.error) {
			return { error: user.error };
		}
		if (reqId !== user.data) {
			return {
				error: {
					code: ERROR_CODES.PERMISSION_ERROR,
					message: "Unauthorized",
					userHint: "You are not authorized to view this user",
					actionable: false,
					detail: "Unauthorized to view user",
				},
			};
		}
		return await this.repo.updateProfileInfo(userInfo);
	}
}
