import { Router, Request, Response, NextFunction } from "express";
import { z } from "zod";
import bcrypt from "bcryptjs";
import { prisma } from "../lib/prisma.js";
import { requireAuth, requireRole } from "../middleware/auth.js";
import { AppError } from "../middleware/errorHandler.js";
import { parsePage, paginatedResponse } from "../lib/pagination.js";
import { AuthenticatedRequest } from "../types/index.js";

const router = Router();
const auth = requireAuth as unknown as (r: Request, s: Response, n: NextFunction) => void;
const managerUp = requireRole("owner", "manager") as unknown as (r: Request, s: Response, n: NextFunction) => void;

// Strip passwordHash from any user object before sending
function safe<T extends { passwordHash?: string }>(u: T): Omit<T, "passwordHash"> {
  const { passwordHash: _, ...rest } = u;
  return rest;
}

const CreateUserSchema = z.object({
  email: z.string().email(),
  password: z.string().min(8),
  name: z.string().min(1).max(100),
  roleId: z.string().min(1),
});

const UpdateUserSchema = z.object({
  name: z.string().min(1).max(100).optional(),
  roleId: z.string().min(1).optional(),
  password: z.string().min(8).optional(),
});

// GET /api/v1/users
router.get("/", auth, managerUp, async (req: Request, res: Response, next: NextFunction) => {
  try {
    const authReq = req as AuthenticatedRequest;
    const { page, limit, skip } = parsePage(req.query as Record<string, unknown>);

    const where = { vendorId: authReq.auth.vendorId };
    const [users, total] = await Promise.all([
      prisma.user.findMany({
        where,
        skip,
        take: limit,
        orderBy: { createdAt: "desc" },
        include: { role: true },
      }),
      prisma.user.count({ where }),
    ]);

    res.json(paginatedResponse(users.map(safe), total, { page, limit, skip }));
  } catch (err) {
    next(err);
  }
});

// GET /api/v1/users/roles/list — must be before /:id
router.get("/roles/list", auth, async (_req: Request, res: Response, next: NextFunction) => {
  try {
    const roles = await prisma.role.findMany({ orderBy: { name: "asc" } });
    res.json(roles);
  } catch (err) {
    next(err);
  }
});

// GET /api/v1/users/:id
router.get("/:id", auth, managerUp, async (req: Request, res: Response, next: NextFunction) => {
  try {
    const authReq = req as AuthenticatedRequest;
    const user = await prisma.user.findFirst({
      where: { id: req.params.id, vendorId: authReq.auth.vendorId },
      include: { role: true },
    });
    if (!user) throw new AppError(404, "NOT_FOUND", "User not found");
    res.json(safe(user));
  } catch (err) {
    next(err);
  }
});

// POST /api/v1/users
router.post("/", auth, managerUp, async (req: Request, res: Response, next: NextFunction) => {
  try {
    const authReq = req as AuthenticatedRequest;
    const body = CreateUserSchema.parse(req.body);

    const existing = await prisma.user.findUnique({ where: { email: body.email } });
    if (existing) throw new AppError(409, "CONFLICT", "Email already in use");

    const role = await prisma.role.findUnique({ where: { id: body.roleId } });
    if (!role) throw new AppError(400, "BAD_REQUEST", "Invalid role");

    // Managers cannot create owners
    if (authReq.auth.roleName === "manager" && role.name === "owner") {
      throw new AppError(403, "FORBIDDEN", "Managers cannot create owner accounts");
    }

    const user = await prisma.user.create({
      data: {
        email: body.email,
        passwordHash: await bcrypt.hash(body.password, 10),
        name: body.name,
        vendorId: authReq.auth.vendorId,
        roleId: body.roleId,
      },
      include: { role: true },
    });

    res.status(201).json(safe(user));
  } catch (err) {
    next(err);
  }
});

// PUT /api/v1/users/:id
router.put("/:id", auth, managerUp, async (req: Request, res: Response, next: NextFunction) => {
  try {
    const authReq = req as AuthenticatedRequest;
    const body = UpdateUserSchema.parse(req.body);

    const existing = await prisma.user.findFirst({
      where: { id: req.params.id, vendorId: authReq.auth.vendorId },
      include: { role: true },
    });
    if (!existing) throw new AppError(404, "NOT_FOUND", "User not found");

    if (body.roleId) {
      const role = await prisma.role.findUnique({ where: { id: body.roleId } });
      if (!role) throw new AppError(400, "BAD_REQUEST", "Invalid role");
      if (authReq.auth.roleName === "manager" && role.name === "owner") {
        throw new AppError(403, "FORBIDDEN", "Managers cannot assign owner role");
      }
    }

    const updateData: Record<string, unknown> = {};
    if (body.name) updateData.name = body.name;
    if (body.roleId) updateData.roleId = body.roleId;
    if (body.password) updateData.passwordHash = await bcrypt.hash(body.password, 10);

    const user = await prisma.user.update({
      where: { id: req.params.id },
      data: updateData,
      include: { role: true },
    });

    res.json(safe(user));
  } catch (err) {
    next(err);
  }
});

// DELETE /api/v1/users/:id
router.delete("/:id", auth, managerUp, async (req: Request, res: Response, next: NextFunction) => {
  try {
    const authReq = req as AuthenticatedRequest;
    if (req.params.id === authReq.auth.userId) {
      throw new AppError(400, "BAD_REQUEST", "Cannot delete your own account");
    }
    const existing = await prisma.user.findFirst({
      where: { id: req.params.id, vendorId: authReq.auth.vendorId },
    });
    if (!existing) throw new AppError(404, "NOT_FOUND", "User not found");

    await prisma.user.delete({ where: { id: req.params.id } });
    res.status(204).send();
  } catch (err) {
    next(err);
  }
});

export default router;
