import express from "express";
import bcrypt from "bcryptjs";

import { requireAuth, requireRole } from "../../middleware/auth.js";
import { validate } from "../../shared/validate.js";
import { httpError } from "../../shared/errors.js";
import { User } from "./user.model.js";
import {
  createUserSchema,
  updateMeSchema,
  updateUserStatusSchema,
  updateUserCategorySchema,
} from "./users.schemas.js";

export const usersRouter = express.Router();

usersRouter.use(requireAuth);

usersRouter.get("/me", async (req, res, next) => {
  try {
    const user = await User.findById(req.user._id, { passwordHash: 0 }).lean();
    if (!user) throw httpError(404, "User not found");
    res.json({ user });
  } catch (err) {
    next(err);
  }
});

usersRouter.patch("/me", validate(updateMeSchema), async (req, res, next) => {
  try {
    const { fullName, phone, category, avatarUrl } = req.validated.body;

    const updates = {};
    if (typeof fullName === "string") updates.fullName = fullName.trim();
    if (typeof phone === "string") updates.phone = phone.trim();
    if (typeof avatarUrl === "string") updates.avatarUrl = avatarUrl.trim();

    const role = req.user?.role;
    const canSetCategory =
      role === "intern" || role === "student" || role === "employee";
    if (canSetCategory && typeof category === "string") {
      updates.category = category;
    }

    const user = await User.findByIdAndUpdate(
      req.user._id,
      { $set: updates },
      { new: true, projection: { passwordHash: 0 } }
    ).lean();

    if (!user) throw httpError(404, "User not found");
    res.json({ user });
  } catch (err) {
    next(err);
  }
});

// Admin-only routes
usersRouter.use(requireRole("admin"));

usersRouter.get("/", async (_req, res, next) => {
  try {
    const users = await User.find({}, { passwordHash: 0 })
      .sort({ createdAt: -1 })
      .lean();
    res.json({ users });
  } catch (err) {
    next(err);
  }
});

usersRouter.post("/", validate(createUserSchema), async (req, res, next) => {
  try {
    const { fullName, email, password, role, category, phone } =
      req.validated.body;
    const exists = await User.findOne({ email: email.toLowerCase().trim() });
    if (exists) throw httpError(409, "Email already exists");

    const passwordHash = await bcrypt.hash(password, 12);
    const user = await User.create({
      fullName,
      email: email.toLowerCase().trim(),
      ...(phone ? { phone: phone.trim() } : {}),
      passwordHash,
      role,
      category: category || "general",
      status: "active",
    });

    res.status(201).json({
      user: {
        id: user._id,
        fullName: user.fullName,
        email: user.email,
        phone: user.phone,
        role: user.role,
        category: user.category,
        status: user.status,
      },
    });
  } catch (err) {
    next(err);
  }
});

usersRouter.patch(
  "/:id/status",
  validate(updateUserStatusSchema),
  async (req, res, next) => {
    try {
      const { id } = req.validated.params;
      const { status } = req.validated.body;

      const user = await User.findByIdAndUpdate(
        id,
        { $set: { status } },
        { new: true, projection: { passwordHash: 0 } }
      ).lean();

      if (!user) throw httpError(404, "User not found");
      res.json({ user });
    } catch (err) {
      next(err);
    }
  }
);

usersRouter.patch(
  "/:id/category",
  validate(updateUserCategorySchema),
  async (req, res, next) => {
    try {
      const { id } = req.validated.params;
      const { category } = req.validated.body;

      const target = await User.findById(id).lean();
      if (!target) throw httpError(404, "User not found");
      if (target.role === "admin") {
        throw httpError(400, "Cannot change category for admin users");
      }

      const user = await User.findByIdAndUpdate(
        id,
        { $set: { category } },
        { new: true, projection: { passwordHash: 0 } }
      ).lean();

      if (!user) throw httpError(404, "User not found");
      res.json({ user });
    } catch (err) {
      next(err);
    }
  }
);
