import { Router, Response } from 'express';
import { authenticate, AuthRequest } from '../middleware/auth';
import { requireRole } from '../middleware/requireRole';
import { validate } from '../middleware/validate';
import {
  UpdateAppointmentSchema,
  UpdateAppointmentStatusSchema,
  AppointmentStatus,
} from '@nobel/shared';
import {
  transitionAppointment,
  convertAppointment,
} from '../services/appointments.service';
import { qstr, qstrDef } from '../lib/query';
import prisma from '../lib/prisma';

const router = Router();
router.use(authenticate, requireRole('admin', 'staff'));

router.get('/', async (req: AuthRequest, res: Response) => {
  const statusFilter = qstr(req, 'status');
  const from = qstr(req, 'from');
  const to = qstr(req, 'to');
  const search = qstr(req, 'search');
  const cursor = qstr(req, 'cursor');
  const take = Math.min(parseInt(qstrDef(req, 'limit', '50')) || 50, 100);

  const where: Record<string, unknown> = {};
  if (statusFilter) where.status = statusFilter;
  const dateRange: Record<string, Date> = {};
  if (from && /^\d{4}-\d{2}-\d{2}$/.test(from)) dateRange.gte = new Date(from);
  if (to && /^\d{4}-\d{2}-\d{2}$/.test(to)) dateRange.lte = new Date(to);
  if (Object.keys(dateRange).length) where.date = dateRange;
  if (search) {
    where.OR = [
      { no: { contains: search, mode: 'insensitive' } },
      { name: { contains: search, mode: 'insensitive' } },
      { phone: { contains: search } },
      { customer: { name: { contains: search, mode: 'insensitive' } } },
    ];
  }

  const [appointments, total] = await Promise.all([
    prisma.appointment.findMany({
      where,
      take: take + 1,
      ...(cursor ? { cursor: { id: cursor }, skip: 1 } : {}),
      orderBy: [{ date: 'asc' }, { slot: 'asc' }, { id: 'asc' }],
      include: {
        service: { select: { id: true, name: true, price: true, durationMin: true, category: true } },
        customer: { select: { id: true, name: true, phone: true } },
        vehicle: { select: { id: true, year: true, make: true, model: true, plate: true } },
        convertedOrder: { select: { id: true, no: true } },
      },
    }),
    prisma.appointment.count({ where }),
  ]);

  const hasMore = appointments.length > take;
  if (hasMore) appointments.pop();

  res.json({
    data: appointments,
    nextCursor: hasMore ? appointments[appointments.length - 1]?.id : null,
    total,
  });
});

router.get('/:id', async (req: AuthRequest, res: Response) => {
  const appt = await prisma.appointment.findUnique({
    where: { id: req.params.id },
    include: {
      service: true,
      customer: { include: { vehicles: { where: { deletedAt: null } } } },
      vehicle: true,
      convertedOrder: { select: { id: true, no: true, status: true } },
    },
  });
  if (!appt) { res.status(404).json({ error: 'Appointment not found' }); return; }
  res.json(appt);
});

router.patch('/:id', validate(UpdateAppointmentSchema), async (req: AuthRequest, res: Response) => {
  const appt = await prisma.appointment.findUniqueOrThrow({ where: { id: req.params.id } });
  if (['completed', 'cancelled', 'converted'].includes(appt.status)) {
    res.status(400).json({ error: 'Closed appointments cannot be edited' });
    return;
  }

  const body = req.body as { date?: string; slot?: string; internalNotes?: string | null; customerId?: string | null; vehicleId?: string | null };

  if (body.customerId) {
    const customer = await prisma.customer.findFirst({ where: { id: body.customerId, deletedAt: null } });
    if (!customer) { res.status(400).json({ error: 'Unknown customer' }); return; }
  }
  if (body.vehicleId) {
    const vehicle = await prisma.vehicle.findFirst({ where: { id: body.vehicleId, deletedAt: null } });
    const owner = body.customerId ?? appt.customerId;
    if (!vehicle || (owner && vehicle.customerId !== owner)) {
      res.status(400).json({ error: 'Vehicle does not belong to this customer' });
      return;
    }
  }

  const updated = await prisma.appointment.update({
    where: { id: req.params.id },
    data: {
      ...(body.date ? { date: new Date(body.date) } : {}),
      ...(body.slot ? { slot: body.slot } : {}),
      ...(body.internalNotes !== undefined ? { internalNotes: body.internalNotes } : {}),
      ...(body.customerId !== undefined ? { customerId: body.customerId } : {}),
      ...(body.vehicleId !== undefined ? { vehicleId: body.vehicleId } : {}),
    },
    include: { service: true, customer: true, vehicle: true },
  });

  await prisma.auditLog.create({
    data: { userId: req.user!.sub, action: 'update_appointment', entityType: 'Appointment', entityId: updated.id },
  });

  res.json(updated);
});

router.patch('/:id/status', validate(UpdateAppointmentStatusSchema), async (req: AuthRequest, res: Response) => {
  const { status, cancelReason } = req.body as { status: AppointmentStatus; cancelReason?: string | null };
  const updated = await transitionAppointment(req.params.id, status, req.user!.sub, cancelReason);
  res.json(updated);
});

router.post('/:id/convert', async (req: AuthRequest, res: Response) => {
  const order = await convertAppointment(req.params.id, req.user!.sub);
  res.status(201).json(order);
});

export default router;
