
import { z } from "zod";
import { router, publicProcedure, protectedProcedure } from "../_core/trpc";
import { getDb } from "../db";
import crypto from "crypto";
import { users } from "../../drizzle/schema";
import { eq } from "drizzle-orm";
import bcrypt from "bcryptjs";
import jwt from "jsonwebtoken";
import { COOKIE_NAME, ONE_YEAR_MS } from "@shared/const";
import { getSessionCookieOptions } from "../_core/cookies";
import { checkRateLimit } from "../_core/security";

const generateToken = (userId: number, role: string) => {
    return jwt.sign({ userId, role }, process.env.JWT_SECRET || "s3cret-leifo-key-change-me", {
        expiresIn: "1y",
    });
};

export const authRouter = router({
    me: publicProcedure.query(({ ctx }) => {
        // console.log("🔍 [Auth] me called, user:", ctx.user?.username);
        if (!ctx.user) return null;
        // eslint-disable-next-line @typescript-eslint/no-unused-vars
        const { password, apiKeyHash, ...safeUser } = ctx.user;
        return safeUser;
    }),

    logout: publicProcedure.mutation(({ ctx }) => {
        const cookieOptions = getSessionCookieOptions(ctx.req);
        ctx.res.clearCookie(COOKIE_NAME, { ...cookieOptions, maxAge: -1 });
        return { success: true };
    }),


    login: publicProcedure
        .input(
            z.object({
                username: z.string(),
                password: z.string(),
            })
        )
        .mutation(async ({ ctx, input }) => {
            const ip = ctx.req.ip || "unknown";
            // Strict Rate Limit for Login: 5 attempts per 15 minutes
            checkRateLimit(`auth:${ip}`, 5, 15 * 60 * 1000);

            const db = await getDb();
            if (!db) throw new Error("Database not available");

            const [user] = await db
                .select()
                .from(users)
                .where(eq(users.username, input.username));

            if (!user) {
                // Timing attack mitigation + error message consistency
                await bcrypt.compare(input.password, "$2a$10$abcdefghijklmnopqrstuvwxyz123456");
                throw new Error("Identifiants incorrects");
            }

            const validPassword = await bcrypt.compare(input.password, user.password);

            if (!validPassword) {
                throw new Error("Invalid username or password");
            }

            // Update last signed in
            await db
                .update(users)
                .set({ lastSignedIn: new Date() })
                .where(eq(users.id, user.id));

            const token = generateToken(user.id, user.role);

            const cookieOptions = getSessionCookieOptions(ctx.req);
            ctx.res.cookie(COOKIE_NAME, token, { ...cookieOptions, maxAge: ONE_YEAR_MS });

            // eslint-disable-next-line @typescript-eslint/no-unused-vars
            const { password, apiKeyHash, ...safeUser } = user;
            return { success: true, user: safeUser };
        }),

    generateApiKey: protectedProcedure.mutation(async ({ ctx }) => {
        const db = await getDb();
        if (!db) throw new Error("Database not available");

        const apiKey = "leifo_" + crypto.randomBytes(24).toString("hex");
        const apiKeyHash = crypto.createHash("sha256").update(apiKey).digest("hex");

        await db
            .update(users)
            .set({ apiKeyHash })
            .where(eq(users.id, ctx.user.id));

        // Return the plain key ONLY ONCE
        return { apiKey };
    }),

    register: publicProcedure
        .input(
            z.object({
                username: z.string().min(3),
                password: z.string().min(6),
                name: z.string().optional(),
                email: z.string().email().optional(),
                secretKey: z.string() // Simple protection for registration
            })
        )
        .mutation(async ({ input }) => {
            // Basic protection to prevent public registration
            if (input.secretKey !== process.env.ADMIN_REGISTER_SECRET) {
                throw new Error("Invalid registration secret");
            }

            const db = await getDb();
            if (!db) throw new Error("Database not available");

            const hashedPassword = await bcrypt.hash(input.password, 10);

            try {
                await db.insert(users).values({
                    username: input.username,
                    password: hashedPassword,
                    name: input.name,
                    email: input.email,
                    role: "admin", // Defaulting to admin for now as requested
                });
                return { success: true };
            } catch (error) {
                // Check for duplicate entry error (roughly)
                throw new Error("Username already exists or other error");
            }
        }),
});
