import { Router, Response } from 'express';
import { authenticate, AuthRequest } from '../middleware/auth';
import { requireRole } from '../middleware/requireRole';
import { validate } from '../middleware/validate';
import {
  CreateOrderSchema,
  UpdateOrderSchema,
  UpdateOrderStatusSchema,
  CreateLineItemSchema,
  UpdateLineItemSchema,
  FinalizeOrderSchema,
} from '@nobel/shared';
import { finalizeOrder, transitionStatus } from '../services/orders.service';
import { nextOrderNo } from '../services/sequences.service';
import { qstr, qstrDef } from '../lib/query';
import prisma from '../lib/prisma';

const router = Router();
router.use(authenticate);

router.get('/', async (req: AuthRequest, res: Response) => {
  const statusFilter = qstr(req, 'status');
  const assignedToIdFilter = qstr(req, 'assignedToId');
  const search = qstr(req, 'search');
  const cursor = qstr(req, 'cursor');
  const take = Math.min(parseInt(qstrDef(req, 'limit', '50')) || 50, 100);
  const { role, sub } = req.user!;

  const where: Record<string, unknown> = {};

  if (role === 'technician') {
    where.assignedToId = sub;
  } else if (assignedToIdFilter) {
    where.assignedToId = assignedToIdFilter;
  }

  if (statusFilter) where.status = statusFilter;

  if (search) {
    where.OR = [
      { no: { contains: search, mode: 'insensitive' } },
      { customer: { name: { contains: search, mode: 'insensitive' } } },
    ];
  }

  const [orders, total] = await Promise.all([
    prisma.workOrder.findMany({
      where,
      take: take + 1,
      ...(cursor ? { cursor: { id: cursor }, skip: 1 } : {}),
      orderBy: { createdAt: 'desc' },
      include: {
        customer: { select: { id: true, name: true } },
        vehicle: { select: { id: true, year: true, make: true, model: true, plate: true } },
        assignedTo: { select: { id: true, name: true } },
        receipt: { select: { id: true, no: true } },
        _count: { select: { items: true } },
      },
    }),
    prisma.workOrder.count({ where }),
  ]);

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

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

router.post('/', requireRole('admin', 'staff'), validate(CreateOrderSchema), async (req: AuthRequest, res: Response) => {
  const shop = await prisma.shop.findFirst();
  const vehicle = await prisma.vehicle.findUnique({ where: { id: req.body.vehicleId as string } });

  if (!vehicle || vehicle.customerId !== req.body.customerId) {
    res.status(400).json({ error: 'Vehicle does not belong to this customer' });
    return;
  }
  if (req.body.assignedToId) {
    const assignee = await prisma.user.findUnique({ where: { id: req.body.assignedToId as string } });
    if (!assignee || !assignee.active || assignee.role !== 'technician') {
      res.status(400).json({ error: 'Assignee must be an active technician' });
      return;
    }
  }

  const no = await nextOrderNo();
  const order = await prisma.workOrder.create({
    data: {
      no,
      customerId: req.body.customerId as string,
      vehicleId: req.body.vehicleId as string,
      assignedToId: (req.body.assignedToId as string | null) || null,
      mileageIn: (req.body.mileageIn as number) ?? vehicle?.miles ?? 0,
      notes: (req.body.notes as string) || '',
      taxRate: shop?.taxRate ?? 0.0875,
      discount: 0,
      status: 'draft',
    },
    include: { customer: true, vehicle: true },
  });

  await prisma.auditLog.create({
    data: { userId: req.user!.sub, action: 'create_order', entityType: 'WorkOrder', entityId: order.id },
  });

  res.status(201).json(order);
});

router.get('/:id', async (req: AuthRequest, res: Response) => {
  const { role, sub } = req.user!;
  const order = await prisma.workOrder.findUnique({
    where: { id: req.params.id },
    include: {
      customer: true,
      vehicle: true,
      assignedTo: { select: { id: true, name: true, role: true } },
      items: {
        include: { inventoryItem: { select: { id: true, sku: true, name: true, stock: true } } },
      },
      receipt: true,
    },
  });

  if (!order) { res.status(404).json({ error: 'Order not found' }); return; }
  if (role === 'technician' && order.assignedToId !== sub) {
    res.status(403).json({ error: 'Access denied' });
    return;
  }

  res.json(order);
});

router.patch('/:id', requireRole('admin', 'staff', 'technician'), async (req: AuthRequest, res: Response) => {
  const { role, sub } = req.user!;
  const order = await prisma.workOrder.findUniqueOrThrow({ where: { id: req.params.id } });

  if (order.status === 'paid') { res.status(400).json({ error: 'Paid orders cannot be edited' }); return; }
  if (role === 'technician' && order.assignedToId !== sub) { res.status(403).json({ error: 'Access denied' }); return; }

  const result = UpdateOrderSchema.safeParse(req.body);
  if (!result.success) {
    res.status(400).json({ error: 'Validation error', details: result.error.flatten().fieldErrors });
    return;
  }

  const updated = await prisma.workOrder.update({
    where: { id: req.params.id },
    data: result.data,
    include: { customer: true, vehicle: true, items: true, receipt: true },
  });
  res.json(updated);
});

router.patch('/:id/status', validate(UpdateOrderStatusSchema), async (req: AuthRequest, res: Response) => {
  const { role, sub } = req.user!;
  const order = await prisma.workOrder.findUniqueOrThrow({ where: { id: req.params.id } });

  if (role === 'technician' && order.assignedToId !== sub) { res.status(403).json({ error: 'Access denied' }); return; }

  const updated = await transitionStatus(req.params.id, String(req.body.status), sub);
  res.json(updated);
});

// Line items
router.post('/:id/items', requireRole('admin', 'staff', 'technician'), async (req: AuthRequest, res: Response) => {
  const { role, sub } = req.user!;
  const order = await prisma.workOrder.findUniqueOrThrow({ where: { id: req.params.id } });

  if (order.status === 'paid') { res.status(400).json({ error: 'Paid orders cannot be edited' }); return; }
  if (role === 'technician' && order.assignedToId !== sub) { res.status(403).json({ error: 'Access denied' }); return; }

  const result = CreateLineItemSchema.safeParse(req.body);
  if (!result.success) {
    res.status(400).json({ error: 'Validation error', details: result.error.flatten().fieldErrors });
    return;
  }

  const item = await prisma.lineItem.create({
    data: { ...result.data, orderId: req.params.id },
    include: { inventoryItem: { select: { id: true, sku: true, name: true, stock: true } } },
  });
  res.status(201).json(item);
});

router.patch('/:id/items/:iid', requireRole('admin', 'staff', 'technician'), async (req: AuthRequest, res: Response) => {
  const { role, sub } = req.user!;
  const order = await prisma.workOrder.findUniqueOrThrow({ where: { id: req.params.id } });

  if (order.status === 'paid') { res.status(400).json({ error: 'Paid orders cannot be edited' }); return; }
  if (role === 'technician' && order.assignedToId !== sub) { res.status(403).json({ error: 'Access denied' }); return; }

  const result = UpdateLineItemSchema.safeParse(req.body);
  if (!result.success) {
    res.status(400).json({ error: 'Validation error', details: result.error.flatten().fieldErrors });
    return;
  }

  const existing = await prisma.lineItem.findFirst({ where: { id: req.params.iid, orderId: req.params.id } });
  if (!existing) { res.status(404).json({ error: 'Line item not found on this order' }); return; }

  const item = await prisma.lineItem.update({
    where: { id: req.params.iid },
    data: result.data,
    include: { inventoryItem: { select: { id: true, sku: true, name: true, stock: true } } },
  });
  res.json(item);
});

router.delete('/:id/items/:iid', requireRole('admin', 'staff', 'technician'), async (req: AuthRequest, res: Response) => {
  const { role, sub } = req.user!;
  const order = await prisma.workOrder.findUniqueOrThrow({ where: { id: req.params.id } });

  if (order.status === 'paid') { res.status(400).json({ error: 'Paid orders cannot be edited' }); return; }
  if (role === 'technician' && order.assignedToId !== sub) { res.status(403).json({ error: 'Access denied' }); return; }

  const deleted = await prisma.lineItem.deleteMany({ where: { id: req.params.iid, orderId: req.params.id } });
  if (deleted.count === 0) { res.status(404).json({ error: 'Line item not found on this order' }); return; }
  res.json({ ok: true });
});

router.post('/:id/finalize', requireRole('admin', 'staff', 'accounts'), validate(FinalizeOrderSchema), async (req: AuthRequest, res: Response) => {
  const receipt = await finalizeOrder(req.params.id, String(req.body.method), req.user!.sub);
  res.json(receipt);
});

// Ask the customer (via their portal link) to approve the current estimate
router.post('/:id/request-approval', requireRole('admin', 'staff'), async (req: AuthRequest, res: Response) => {
  const order = await prisma.workOrder.findUniqueOrThrow({ where: { id: req.params.id }, include: { items: true } });

  if (['paid', 'closed'].includes(order.status)) {
    res.status(400).json({ error: 'Cannot request approval on a finalized order' });
    return;
  }
  if (order.items.length === 0) {
    res.status(400).json({ error: 'Add line items before requesting approval' });
    return;
  }

  const updated = await prisma.workOrder.update({
    where: { id: order.id },
    data: { approvalStatus: 'pending', approvalAt: null, declineReason: null },
  });

  await prisma.auditLog.create({
    data: { userId: req.user!.sub, action: 'request_approval', entityType: 'WorkOrder', entityId: order.id },
  });

  res.json(updated);
});

export default router;
