import { prisma } from '@/lib/prisma';
import { sanitizeText } from '@/lib/sanitize';
import type { CreateOrderInput, AddItemsInput } from '@/schemas/order.schema';
import { OrderStatus } from '@prisma/client';

export async function listOrders(filters: {
  status?: string;
  tableId?: string;
  waiterId?: string;
  page?: number;
  limit?: number;
  from?: string;
  to?: string;
}) {
  const page = filters.page || 1;
  const limit = filters.limit || 20;
  const skip = (page - 1) * limit;

  const where = {
    ...(filters.status
      ? filters.status.includes(',')
        ? { status: { in: filters.status.split(',') as OrderStatus[] } }
        : { status: filters.status as OrderStatus }
      : { status: { notIn: [OrderStatus.CANCELLED] } }),
    ...(filters.tableId ? { tableId: filters.tableId } : {}),
    ...(filters.waiterId ? { waiterId: filters.waiterId } : {}),
    ...(filters.from || filters.to
      ? {
          createdAt: {
            ...(filters.from ? { gte: new Date(filters.from) } : {}),
            ...(filters.to ? { lte: new Date(filters.to) } : {}),
          },
        }
      : {}),
  };

  const [orders, total] = await prisma.$transaction([
    prisma.order.findMany({
      where,
      skip,
      take: limit,
      include: {
        table: { select: { id: true, number: true, zone: { select: { name: true } } } },
        waiter: { select: { id: true, name: true } },
        items: {
          include: { product: { select: { id: true, name: true } } },
        },
        _count: { select: { items: true } },
      },
      orderBy: { createdAt: 'desc' },
    }),
    prisma.order.count({ where }),
  ]);

  return { orders, total, page, limit };
}

export async function getOrderById(id: string) {
  return prisma.order.findUnique({
    where: { id },
    include: {
      table: { select: { id: true, number: true, zone: { select: { name: true } } } },
      waiter: { select: { id: true, name: true } },
      items: {
        include: { product: { select: { id: true, name: true, type: true, unitMeasure: true } } },
        orderBy: { createdAt: 'asc' },
      },
      invoices: { select: { id: true, invoiceNumber: true, status: true, total: true } },
    },
  });
}

export async function createOrder(data: CreateOrderInput, waiterId: string) {
  return prisma.$transaction(async (tx) => {
    // Validar mesa si se proveyó
    if (data.tableId) {
      const table = await tx.table.findUnique({ where: { id: data.tableId } });
      if (!table) throw new Error('TABLE_NOT_FOUND');
      await tx.table.update({ where: { id: data.tableId }, data: { status: 'OCCUPIED' } });
    }

    // Obtener productos disponibles
    const productIds = data.items.map((i) => i.productId);
    const products = await tx.product.findMany({
      where: { id: { in: productIds }, isAvailable: true },
      select: { id: true, name: true, price: true },
    });

    if (products.length !== productIds.length) throw new Error('ONE_OR_MORE_PRODUCTS_UNAVAILABLE');

    const productMap = new Map(products.map((p) => [p.id, p]));
    let totalAmount = 0;

    const itemsToCreate = data.items.map((item) => {
      const prod = productMap.get(item.productId)!;
      const subtotal = Number(prod.price) * item.quantity;
      totalAmount += subtotal;
      return {
        productId: item.productId,
        quantity: item.quantity,
        unitPrice: prod.price,
        subtotal,
        notes: item.notes ? sanitizeText(item.notes) : undefined,
        status: 'PENDING' as const,
      };
    });

    return tx.order.create({
      data: {
        tableId: data.tableId,
        waiterId,
        customerName: data.customerName ? sanitizeText(data.customerName) : undefined,
        notes: data.notes ? sanitizeText(data.notes) : undefined,
        totalAmount,
        status: 'OPEN',
        items: { create: itemsToCreate },
      },
      include: {
        table: { select: { id: true, number: true } },
        waiter: { select: { id: true, name: true } },
        items: { include: { product: { select: { id: true, name: true } } } },
      },
    });
  });
}

function assertOrderMutationAllowed(order: { waiterId: string | null }, callerId: string, callerRole: string) {
  const isOwner = order.waiterId === callerId;
  const isManager = callerRole === 'ADMIN' || callerRole === 'MANAGER';
  if (!isOwner && !isManager) throw new Error('FORBIDDEN_ORDER_OWNER');
}

export async function addItemsToOrder(
  orderId: string,
  data: AddItemsInput,
  callerId: string,
  callerRole: string
) {
  return prisma.$transaction(async (tx) => {
    const order = await tx.order.findUnique({ where: { id: orderId }, select: { id: true, status: true, totalAmount: true, waiterId: true } });
    if (!order) throw new Error('ORDER_NOT_FOUND');
    assertOrderMutationAllowed(order, callerId, callerRole);
    if (order.status === 'BILLED' || order.status === 'CANCELLED')
      throw new Error('INVALID_STATUS: No se pueden agregar ítems a una orden ya facturada o cancelada');

    const productIds = data.items.map((i) => i.productId);
    const products = await tx.product.findMany({
      where: { id: { in: productIds }, isAvailable: true },
      select: { id: true, price: true },
    });

    if (products.length !== productIds.length) throw new Error('ONE_OR_MORE_PRODUCTS_UNAVAILABLE');
    const productMap = new Map(products.map((p) => [p.id, p]));

    let addedAmount = 0;
    const itemsToCreate = data.items.map((item) => {
      const prod = productMap.get(item.productId)!;
      const subtotal = Number(prod.price) * item.quantity;
      addedAmount += subtotal;
      return {
        orderId,
        productId: item.productId,
        quantity: item.quantity,
        unitPrice: prod.price,
        subtotal,
        notes: item.notes ? sanitizeText(item.notes) : undefined,
        status: 'PENDING' as const,
      };
    });

    await tx.orderItem.createMany({ data: itemsToCreate });
    return tx.order.update({
      where: { id: orderId },
      data: { totalAmount: { increment: addedAmount }, status: 'IN_PROGRESS' },
      include: { items: { include: { product: { select: { id: true, name: true } } } } },
    });
  });
}

export async function cancelOrder(orderId: string, reason: string, userId: string, callerRole: string) {
  return prisma.$transaction(async (tx) => {
    const order = await tx.order.findUnique({
      where: { id: orderId },
      include: { items: true, table: true },
    });
    if (!order) throw new Error('ORDER_NOT_FOUND');
    assertOrderMutationAllowed(order, userId, callerRole);
    if (order.status === 'BILLED' || order.status === 'CANCELLED')
      throw new Error('INVALID_STATUS: No se puede cancelar una orden ya facturada o cancelada');

    // Cancelar todos los ítems pendientes
    await tx.orderItem.updateMany({
      where: { orderId, status: { in: ['PENDING', 'PREPARING'] } },
      data: { status: 'CANCELLED', cancelledAt: new Date(), cancelReason: sanitizeText(reason) },
    });

    // Liberar mesa si estaba ocupada
    if (order.tableId && order.table?.status === 'OCCUPIED') {
      await tx.table.update({ where: { id: order.tableId }, data: { status: 'AVAILABLE' } });
    }

    return tx.order.update({
      where: { id: orderId },
      data: { status: 'CANCELLED' },
      select: { id: true, status: true },
    });
  });
}
