import { logError } from "@/lib/logger";
import bcrypt from "bcrypt";
import type { Context } from "hono";
import type { Generated, Kysely } from "kysely";
import type { DB } from "../../datastore/db";

export interface ConsoleUsers {
    id: Generated<string>;
    first_name: string;
    last_name: string | null;
    email: string;
    username: string;
    hashed_password: string;
    password_salt: string;
    password_iterations: number;
    password_hash_algo: string;
    login_attempts: Generated<number>;
    login_lock_release_at: Date | null;
    is_active: Generated<boolean>;
    is_deleted: Generated<boolean>;
    created_at: Generated<Date>;
    updated_at: Date | null;
}

interface Variables {
    datastore: Kysely<DB & { console_users: ConsoleUsers }>;
}

export interface AdminConsoleUserRepositoryContext
    extends Context<{ Variables: Variables }> { }

export class AdminConsoleUserRepository {
    private datastore: Variables["datastore"];
    constructor(ctx: AdminConsoleUserRepositoryContext) {
        this.datastore = ctx.get("datastore") as unknown as Kysely<DB & { console_users: ConsoleUsers }>;
    }

    async getEntryByEmail(email: string) {
        try {
            // @ts-ignore: console_users may not be in DB type yet
            return await this.datastore
                .selectFrom("console_users")
                .selectAll()
                .where("email", "=", email.toLowerCase())
                .where("is_deleted", "=", false)
                .executeTakeFirst();
        } catch (error) {
            logError(error as string);
            return null;
        }
    }

    async getEntryById(pid: string) {
        try {
            return await this.datastore
                .selectFrom("console_users")
                .selectAll()
                .where("id", "=", pid)
                .executeTakeFirst();
        } catch (error) {
            logError(error as string);
            return null;
        }
    }

    async createEntry(email: string, passwordPlain: string, firstName: string, lastName: string, username: string) {
        try {
            const salt = await bcrypt.genSalt(10);
            const hashedPassword = await bcrypt.hash(passwordPlain, salt);
            return await this.datastore
                .insertInto("console_users")
                .values({
                    email: email.toLowerCase(),
                    first_name: firstName,
                    last_name: lastName || null,
                    username: username || email.split("@")[0],
                    hashed_password: hashedPassword,
                    password_salt: salt,
                    password_iterations: 10,
                    password_hash_algo: 'bcrypt',
                })
                .returningAll()
                .executeTakeFirst();
        } catch (error) {
            logError(error as string);
            throw error;
        }
    }

    async updateEntry(pid: string, data: any) {
        try {
            if (data.password) {
                data.hashed_password = await bcrypt.hash(data.password, 10);
                delete data.password;
            }
            return await this.datastore
                .updateTable("console_users")
                .set(data)
                .where("id", "=", pid)
                .returningAll()
                .executeTakeFirst();
        } catch (error) {
            logError(error as string);
            return null;
        }
    }

    async searchEntries(query: string, page: number = 1, limit: number = 10) {
        try {
            let stmt = this.datastore
                .selectFrom("console_users")
                .selectAll()
                .where("is_deleted", "=", false);

            if (query) {
                stmt = stmt.where("email", "ilike", `%${query}%`);
            }

            const offset = (page - 1) * limit;

            const users = await stmt.limit(limit).offset(offset).execute();
            // Count total
            const countResult = await this.datastore
                .selectFrom("console_users")
                .select((eb) => eb.fn.count("id").as("total"))
                .where("is_deleted", "=", false) // Apply same filter for count
                // Note: 'ilike' query for count might be needed if exact count of search results is required,
                // but for simplicity in this generated code I'll omit complex count logic for search query for now unless requested.
                // Actually, to be correct, I should apply the same filter.
                .$if(!!query, (qb) => qb.where("email", "ilike", `%${query}%`))
                .executeTakeFirst();

            const total = Number(countResult?.total || 0);

            return { users, total, page, limit, totalPages: Math.ceil(total / limit) };
        } catch (error) {
            logError(error as string);
            return { users: [], total: 0, page, limit, totalPages: 0 };
        }
    }

    async deleteEntry(pid: string) {
        try {
            // Soft delete usually
            return await this.datastore
                .updateTable("console_users")
                .set({ is_deleted: true })
                .where("id", "=", pid)
                .returningAll()
                .executeTakeFirst();
        } catch (error) {
            logError(error as string);
            return null;
        }
    }
}
