import { Router } from 'express';
import bcrypt from 'bcryptjs';
import jwt from 'jsonwebtoken';
import { z } from 'zod';
import { config } from '../config';
import { prisma } from '../db';
import { badRequest, conflict, unauthorized } from '../lib/errors';
import { systemRoleFromDb } from '../lib/roles';
import { AuthedRequest, requireAuth } from '../middleware/auth';

export const authRouter = Router();

const loginSchema = z.object({
  email: z.string().email(),
  password: z.string().min(1),
});

const phoneSchema = z
  .string()
  .trim()
  .optional()
  .nullable()
  .refine((v) => {
    if (v == null || v === '') return true;
    return /^\+?\d{1,10}$/.test(v);
  }, 'Phone: use + and digits only (max 10 digits)');

const profileSchema = z.object({
  name: z.string().trim().min(1, 'Name is required'),
  email: z.string().trim().email('Enter a valid email'),
  phone: phoneSchema,
});

function serializeAuthUser(emp: {
  id: string;
  code: string;
  name: string;
  email: string;
  role: Parameters<typeof systemRoleFromDb>[0];
  active: boolean;
  avatarColor: string;
  phone?: string | null;
  department?: string | null;
  designation?: string | null;
  createdAt?: Date;
}) {
  return {
    id: emp.id,
    code: emp.code,
    name: emp.name,
    email: emp.email,
    phone: emp.phone || '',
    role: systemRoleFromDb(emp.role),
    active: emp.active,
    avatarColor: emp.avatarColor,
    department: emp.department || '',
    designation: emp.designation || '',
    createdAt: emp.createdAt ? emp.createdAt.toISOString() : '',
  };
}

authRouter.post('/login', async (req, res, next) => {
  try {
    const body = loginSchema.parse(req.body);
    const emp = await prisma.employee.findUnique({ where: { email: body.email.toLowerCase() } });
    if (!emp || !emp.active) throw unauthorized('Invalid email or password');
    const ok = await bcrypt.compare(body.password, emp.passwordHash);
    if (!ok) throw unauthorized('Invalid email or password');

    const token = jwt.sign({ sub: emp.id }, config.jwtSecret, {
      expiresIn: config.jwtExpiresIn as jwt.SignOptions['expiresIn'],
    });

    res.json({
      token,
      user: serializeAuthUser(emp),
    });
  } catch (err) {
    next(err);
  }
});

authRouter.get('/me', requireAuth, async (req: AuthedRequest, res, next) => {
  try {
    if (!req.user) throw unauthorized();
    const emp = await prisma.employee.findUnique({ where: { id: req.user.id } });
    if (!emp) throw unauthorized();
    res.json({ user: serializeAuthUser(emp) });
  } catch (err) {
    next(err);
  }
});

authRouter.patch('/me', requireAuth, async (req: AuthedRequest, res, next) => {
  try {
    if (!req.user) throw unauthorized();
    const body = profileSchema.parse(req.body);
    const email = body.email.toLowerCase();
    const phone = body.phone ? String(body.phone).trim() : null;

    const clash = await prisma.employee.findFirst({
      where: {
        email,
        NOT: { id: req.user.id },
      },
    });
    if (clash) throw conflict('Email already in use');

    const emp = await prisma.employee.update({
      where: { id: req.user.id },
      data: {
        name: body.name,
        email,
        phone: phone || null,
      },
    });

    res.json({ user: serializeAuthUser(emp) });
  } catch (err) {
    next(err);
  }
});

authRouter.post('/logout', requireAuth, (_req, res) => {
  // JWT is client-held; client discards token. Endpoint exists for symmetry.
  res.json({ ok: true });
});

authRouter.post('/change-password', requireAuth, async (req: AuthedRequest, res, next) => {
  try {
    const schema = z.object({
      currentPassword: z.string().min(1),
      newPassword: z.string().min(8),
    });
    const body = schema.parse(req.body);
    if (!req.user) throw unauthorized();
    const emp = await prisma.employee.findUnique({ where: { id: req.user.id } });
    if (!emp) throw unauthorized();
    const ok = await bcrypt.compare(body.currentPassword, emp.passwordHash);
    if (!ok) throw badRequest('Current password is incorrect');
    const passwordHash = await bcrypt.hash(body.newPassword, 10);
    await prisma.employee.update({
      where: { id: emp.id },
      data: { passwordHash },
    });
    res.json({ ok: true });
  } catch (err) {
    next(err);
  }
});
