import { Router, type IRouter } from "express";
import bcrypt from "bcryptjs";
import { eq } from "drizzle-orm";
import { db } from "@workspace/db";
import {
  usersTable,
  userRolesTable,
  rolesTable,
  appSettingsTable,
} from "@workspace/db";
import { requireAuth, getUserRoles } from "../middlewares/auth";
import { RegisterBody, LoginBody, UpdateLanguageBody } from "@workspace/api-zod";

const router: IRouter = Router();

function buildAuthUser(user: typeof usersTable.$inferSelect, roles: string[]) {
  return {
    id: user.id,
    username: user.username,
    email: user.email,
    displayNameAr: user.displayNameAr,
    displayNameEn: user.displayNameEn,
    preferredLanguage: user.preferredLanguage,
    avatarUrl: user.avatarUrl,
    isActive: user.isActive,
    roles,
    createdAt: user.createdAt,
  };
}

router.post("/auth/register", async (req, res): Promise<void> => {
  const parsed = RegisterBody.safeParse(req.body);
  if (!parsed.success) {
    res.status(400).json({ error: parsed.error.message });
    return;
  }

  const [settings] = await db.select().from(appSettingsTable).where(eq(appSettingsTable.id, 1));
  if (settings && !settings.registrationOpen) {
    res.status(403).json({ error: "Registration is closed" });
    return;
  }

  const { username, email, password, displayNameAr, displayNameEn, preferredLanguage } = parsed.data;

  const existing = await db
    .select()
    .from(usersTable)
    .where(eq(usersTable.username, username));

  if (existing.length > 0) {
    res.status(400).json({ error: "Username already taken" });
    return;
  }

  const passwordHash = await bcrypt.hash(password, 10);

  const [user] = await db
    .insert(usersTable)
    .values({
      username,
      email,
      passwordHash,
      displayNameAr: displayNameAr ?? null,
      displayNameEn: displayNameEn ?? null,
      preferredLanguage: preferredLanguage ?? "ar",
    })
    .returning();

  const [userRole] = await db.select().from(rolesTable).where(eq(rolesTable.name, "user"));
  if (userRole) {
    await db.insert(userRolesTable).values({ userId: user.id, roleId: userRole.id });
  }

  req.session.userId = user.id;
  const roles = await getUserRoles(user.id);
  res.status(201).json(buildAuthUser(user, roles));
});

router.post("/auth/login", async (req, res): Promise<void> => {
  const parsed = LoginBody.safeParse(req.body);
  if (!parsed.success) {
    res.status(400).json({ error: parsed.error.message });
    return;
  }

  const { username, password } = parsed.data;

  const [user] = await db
    .select()
    .from(usersTable)
    .where(eq(usersTable.username, username));

  if (!user) {
    res.status(401).json({ error: "Invalid credentials" });
    return;
  }

  const valid = await bcrypt.compare(password, user.passwordHash);
  if (!valid) {
    res.status(401).json({ error: "Invalid credentials" });
    return;
  }

  if (!user.isActive) {
    res.status(401).json({ error: "Account is inactive" });
    return;
  }

  req.session.userId = user.id;
  const roles = await getUserRoles(user.id);
  res.json(buildAuthUser(user, roles));
});

router.post("/auth/logout", (req, res): void => {
  req.session.destroy(() => {
    res.json({ success: true });
  });
});

router.get("/auth/me", requireAuth, async (req, res): Promise<void> => {
  const [user] = await db
    .select()
    .from(usersTable)
    .where(eq(usersTable.id, req.session.userId!));

  if (!user) {
    res.status(401).json({ error: "User not found" });
    return;
  }

  const roles = await getUserRoles(user.id);
  res.json(buildAuthUser(user, roles));
});

router.patch("/auth/me/language", requireAuth, async (req, res): Promise<void> => {
  const parsed = UpdateLanguageBody.safeParse(req.body);
  if (!parsed.success) {
    res.status(400).json({ error: parsed.error.message });
    return;
  }

  const [user] = await db
    .update(usersTable)
    .set({ preferredLanguage: parsed.data.language })
    .where(eq(usersTable.id, req.session.userId!))
    .returning();

  const roles = await getUserRoles(user.id);
  res.json(buildAuthUser(user, roles));
});

export default router;
