import { createHash, timingSafeEqual } from "crypto";
import { COOKIE_NAME } from "@shared/const";
import { ONE_YEAR_MS } from "@shared/const";
import { z } from "zod";
import { getSessionCookieOptions } from "./_core/cookies";
import { sdk } from "./_core/sdk";
import { systemRouter } from "./_core/systemRouter";
import { publicProcedure, router } from "./_core/trpc";
import { getUserByOpenId, getUserByUsername, upsertUser } from "./db";
import { accountRouter } from "./routers/account";
import { studentRecordsRouter } from "./routers/studentRecords";
import { userManagementRouter } from "./routers/userManagement";
import { verifyPassword } from "./security/passwords";

function fixedTimeMatches(value: string, secret: string) {
  const valueHash = createHash("sha256").update(value).digest();
  const secretHash = createHash("sha256").update(secret).digest();
  return timingSafeEqual(valueHash, secretHash);
}

export const appRouter = router({
    // if you need to use socket.io, read and register route in server/_core/index.ts, all api should start with '/api/' so that the gateway can route correctly
  system: systemRouter,
  auth: router({
    me: publicProcedure.query(opts => opts.ctx.user),
    status: publicProcedure.query(opts => ({ user: opts.ctx.user ?? null })),
    logout: publicProcedure.mutation(({ ctx }) => {
      const cookieOptions = getSessionCookieOptions(ctx.req);
      ctx.res.clearCookie(COOKIE_NAME, { ...cookieOptions, maxAge: -1 });
      return {
        success: true,
      } as const;
    }),
  }),
  localAuth: router({
    enabled: publicProcedure.query(() => process.env.LOCAL_AUTH_ENABLED === "true"),
    login: publicProcedure.input(z.object({ username: z.string().trim().min(1).max(64), password: z.string().min(1).max(1024) })).mutation(async ({ input, ctx }) => {
      const configuredUsername = process.env.LOCAL_ADMIN_USERNAME;
      const configuredPassword = process.env.LOCAL_ADMIN_PASSWORD;
      if (process.env.LOCAL_AUTH_ENABLED !== "true" || !configuredUsername || !configuredPassword) {
        throw new Error("تسجيل الدخول المحلي غير مفعّل أو غير مكتمل الإعداد على هذا الخادم.");
      }
      let openId: string;
      let displayName: string;

      if (fixedTimeMatches(input.username, configuredUsername) && fixedTimeMatches(input.password, configuredPassword)) {
        openId = "local-admin";
        displayName = configuredUsername;
        await upsertUser({ openId, username: configuredUsername, name: configuredUsername, loginMethod: "local", role: "admin", isActive: true, lastSignedIn: new Date() });
        const admin = await getUserByOpenId(openId);
        if (!admin) throw new Error("تعذر تجهيز حساب المدير المحلي.");
      } else {
        const user = await getUserByUsername(input.username);
        if (!user || !user.isActive || !verifyPassword(input.password, user.passwordHash)) {
          throw new Error("بيانات الدخول غير صحيحة.");
        }
        openId = user.openId;
        displayName = user.name || user.username || "مستخدم";
        await upsertUser({ openId: user.openId, lastSignedIn: new Date() });
      }

      const sessionToken = await sdk.createSessionToken(openId, { name: displayName });
      ctx.res.cookie(COOKIE_NAME, sessionToken, { ...getSessionCookieOptions(ctx.req), maxAge: ONE_YEAR_MS });
      return { success: true } as const;
    }),
  }),
  students: studentRecordsRouter,
  account: accountRouter,
  userManagement: userManagementRouter,
});

export type AppRouter = typeof appRouter;
