import { Router, type IRouter } from "express";
import { eq } from "drizzle-orm";
import { db } from "@workspace/db";
import { usersTable } from "@workspace/db";
import { requireAuth, requireAdmin, getUserRoles } from "../middlewares/auth";
import {
  RegisterBody,
  GetUserParams,
  UpdateUserParams,
  UpdateUserBody,
  DeleteUserParams,
} from "@workspace/api-zod";
import bcrypt from "bcryptjs";

const router: IRouter = Router();

router.get("/users/directory", requireAuth, async (_req, res): Promise<void> => {
  const users = await db
    .select({
      id: usersTable.id,
      username: usersTable.username,
      displayNameAr: usersTable.displayNameAr,
      displayNameEn: usersTable.displayNameEn,
      avatarUrl: usersTable.avatarUrl,
      isActive: usersTable.isActive,
    })
    .from(usersTable)
    .where(eq(usersTable.isActive, true));
  res.json(users);
});

function buildUserProfile(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("/users", requireAdmin, async (req, res): Promise<void> => {
  const parsed = RegisterBody.safeParse(req.body);
  if (!parsed.success) {
    res.status(400).json({ error: parsed.error.message });
    return;
  }

  const existing = await db
    .select({ id: usersTable.id })
    .from(usersTable)
    .where(eq(usersTable.username, parsed.data.username));

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

  const passwordHash = await bcrypt.hash(parsed.data.password, 12);

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

  if (!user) {
    res.status(500).json({ error: "Failed to create user" });
    return;
  }

  res.status(201).json(buildUserProfile(user, []));
});

router.get("/users", requireAdmin, async (_req, res): Promise<void> => {
  const users = await db.select().from(usersTable).orderBy(usersTable.createdAt);
  const usersWithRoles = await Promise.all(
    users.map(async (u) => {
      const roles = await getUserRoles(u.id);
      return buildUserProfile(u, roles);
    }),
  );
  res.json(usersWithRoles);
});

router.get("/users/:id", requireAdmin, async (req, res): Promise<void> => {
  const params = GetUserParams.safeParse(req.params);
  if (!params.success) {
    res.status(400).json({ error: params.error.message });
    return;
  }

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

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

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

router.patch("/users/:id", requireAdmin, async (req, res): Promise<void> => {
  const params = UpdateUserParams.safeParse(req.params);
  if (!params.success) {
    res.status(400).json({ error: params.error.message });
    return;
  }

  const parsed = UpdateUserBody.safeParse(req.body);
  if (!parsed.success) {
    res.status(400).json({ error: parsed.error.message });
    return;
  }

  const updateData: Partial<typeof usersTable.$inferInsert> = {};
  if (parsed.data.displayNameAr !== undefined) updateData.displayNameAr = parsed.data.displayNameAr;
  if (parsed.data.displayNameEn !== undefined) updateData.displayNameEn = parsed.data.displayNameEn;
  if (parsed.data.isActive !== undefined) updateData.isActive = parsed.data.isActive;
  if (parsed.data.preferredLanguage !== undefined) updateData.preferredLanguage = parsed.data.preferredLanguage;

  const [user] = await db
    .update(usersTable)
    .set(updateData)
    .where(eq(usersTable.id, params.data.id))
    .returning();

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

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

router.delete("/users/:id", requireAdmin, async (req, res): Promise<void> => {
  const params = DeleteUserParams.safeParse(req.params);
  if (!params.success) {
    res.status(400).json({ error: params.error.message });
    return;
  }

  const [user] = await db
    .delete(usersTable)
    .where(eq(usersTable.id, params.data.id))
    .returning();

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

  res.sendStatus(204);
});

export default router;
