import {
  BadRequestException,
  Injectable,
  NotFoundException,
} from '@nestjs/common';
import { $Enums, compra_asistida_estado } from '@prisma/client';
import { PrismaService } from 'src/prisma/prisma.service';
import { PagoLinkService } from 'src/bancard/pago-link.service';
import { buildPagoLinkUrl } from 'src/bancard/pago-publico.controller';

// Transiciones manuales del operador.
// pago_recibido: solo via webhook Bancard (BancardPostPagoHook)
// facturado:     solo via endpoint POST /compra-asistida/:id/facturar
// cancelado post-pago: requiere permiso CA_REFUND
const { borrador, cotizado, pago_recibido, en_compra_china, en_transito, en_aduana, entregado, facturado, cancelado } = compra_asistida_estado;

const TRANSICIONES: Record<compra_asistida_estado, compra_asistida_estado[]> = {
  [borrador]:        [cotizado, cancelado],
  [cotizado]:        [borrador, cancelado],
  [pago_recibido]:   [en_compra_china],
  [en_compra_china]: [en_transito],
  [en_transito]:     [en_aduana],
  [en_aduana]:       [entregado],
  [entregado]:       [],
  [facturado]:       [],
  [cancelado]:       [],
};

const SELECT_CA = {
  id: true,
  numero: true,
  estado: true,
  cotizacion_usd_gs: true,
  servicio_gs: true,
  logistica_gs: true,
  comision_gs: true,
  notas: true,
  pago_bancard_id: true,
  factura_id: true,
  fecha_entrega_estimada: true,
  created_at: true,
  updated_at: true,
  cliente: {
    select: {
      id: true,
      nombre_fantasia: true,
      personas: { select: { razon_social: true, ruc: true, dv: true } },
    },
  },
  items: {
    orderBy: { orden: 'asc' as const },
    select: {
      id: true,
      descripcion: true,
      url_producto: true,
      proveedor_china: true,
      cantidad: true,
      precio_usd: true,
      orden: true,
    },
  },
} as const;

export interface ItemInput {
  descripcion: string;
  url_producto?: string;
  proveedor_china?: string;
  cantidad: number;
  precio_usd: number;
  orden?: number;
}

export interface CreateCAInput {
  cliente_id?: string;
  cotizacion_usd_gs?: number;
  servicio_gs?: number;
  logistica_gs?: number;
  comision_gs?: number;
  notas?: string;
  fecha_entrega_estimada?: string;
  items: ItemInput[];
}

export interface EditCAInput {
  cliente_id?: string;
  cotizacion_usd_gs?: number;
  servicio_gs?: number;
  logistica_gs?: number;
  comision_gs?: number;
  notas?: string;
  fecha_entrega_estimada?: string;
  items?: ItemInput[];
}

@Injectable()
export class CompraAsistidaService {
  constructor(
    private readonly prisma: PrismaService,
    private readonly pagoLinkService: PagoLinkService,
  ) {}

  async listar(
    empresaId: string,
    filters: {
      estado?: string;
      clienteId?: string;
      search?: string;
      page?: number;
      limit?: number;
    },
  ) {
    const { estado, clienteId, search, page = 1, limit = 20 } = filters;
    const skip = (page - 1) * limit;

    const where: any = { empresa_id: empresaId };

    if (estado) where.estado = estado;
    if (clienteId) where.cliente_id = clienteId;

    if (search) {
      where.OR = [
        { numero: { contains: search, mode: 'insensitive' } },
        { cliente: { nombre_fantasia: { contains: search, mode: 'insensitive' } } },
        {
          cliente: {
            personas: { razon_social: { contains: search, mode: 'insensitive' } },
          },
        },
      ];
    }

    const [data, total] = await Promise.all([
      this.prisma.compra_asistida.findMany({
        where,
        select: SELECT_CA,
        orderBy: { created_at: 'desc' },
        skip,
        take: limit,
      }),
      this.prisma.compra_asistida.count({ where }),
    ]);

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

  async getById(id: string, empresaId: string) {
    const ca = await this.prisma.compra_asistida.findFirst({
      where: { id, empresa_id: empresaId },
      select: SELECT_CA,
    });
    if (!ca) throw new NotFoundException('Compra asistida no encontrada');
    return ca;
  }

  async crear(empresaId: string, _usuarioId: string, data: CreateCAInput) {
    if (!data.items || data.items.length === 0) {
      throw new BadRequestException('Se requiere al menos un ítem');
    }

    const numero = await this.generarNumero(empresaId);

    return this.prisma.compra_asistida.create({
      data: {
        empresa_id: empresaId,
        numero,
        cliente_id: data.cliente_id ?? null,
        estado: borrador,
        cotizacion_usd_gs: data.cotizacion_usd_gs ?? 7500,
        servicio_gs: data.servicio_gs ?? 0,
        logistica_gs: data.logistica_gs ?? 0,
        comision_gs: data.comision_gs ?? 0,
        notas: data.notas ?? null,
        fecha_entrega_estimada: data.fecha_entrega_estimada
          ? new Date(data.fecha_entrega_estimada)
          : null,
        items: {
          create: data.items.map((item, idx) => ({
            descripcion: item.descripcion,
            url_producto: item.url_producto ?? null,
            proveedor_china: item.proveedor_china ?? null,
            cantidad: item.cantidad,
            precio_usd: item.precio_usd,
            orden: item.orden ?? idx,
          })),
        },
      },
      select: SELECT_CA,
    });
  }

  async editar(id: string, empresaId: string, data: EditCAInput) {
    const ca = await this.getById(id, empresaId);

    if (ca.estado !== borrador && ca.estado !== cotizado) {
      throw new BadRequestException(
        'Solo se puede editar en estado borrador o cotizado',
      );
    }

    const updateData: any = {
      updated_at: new Date(),
    };

    if (data.cliente_id !== undefined) updateData.cliente_id = data.cliente_id;
    if (data.cotizacion_usd_gs !== undefined) updateData.cotizacion_usd_gs = data.cotizacion_usd_gs;
    if (data.servicio_gs !== undefined) updateData.servicio_gs = data.servicio_gs;
    if (data.logistica_gs !== undefined) updateData.logistica_gs = data.logistica_gs;
    if (data.comision_gs !== undefined) updateData.comision_gs = data.comision_gs;
    if (data.notas !== undefined) updateData.notas = data.notas;
    if (data.fecha_entrega_estimada !== undefined) {
      updateData.fecha_entrega_estimada = data.fecha_entrega_estimada
        ? new Date(data.fecha_entrega_estimada)
        : null;
    }

    // Reemplazar items si se envían
    if (data.items && data.items.length > 0) {
      updateData.items = {
        deleteMany: {},
        create: data.items.map((item, idx) => ({
          descripcion: item.descripcion,
          url_producto: item.url_producto ?? null,
          proveedor_china: item.proveedor_china ?? null,
          cantidad: item.cantidad,
          precio_usd: item.precio_usd,
          orden: item.orden ?? idx,
        })),
      };
    }

    return this.prisma.compra_asistida.update({
      where: { id },
      data: updateData,
      select: SELECT_CA,
    });
  }

  async avanzarEstado(id: string, empresaId: string, nuevoEstadoRaw: string) {
    const ca = await this.getById(id, empresaId);
    const estadoActual = ca.estado;

    const validos = TRANSICIONES[estadoActual] ?? [];
    if (!validos.includes(nuevoEstadoRaw as compra_asistida_estado)) {
      throw new BadRequestException(
        `Transición inválida: ${estadoActual} → ${nuevoEstadoRaw}. Permitidas: [${validos.join(', ')}]`,
      );
    }

    return this.prisma.compra_asistida.update({
      where: { id },
      data: { estado: nuevoEstadoRaw as compra_asistida_estado, updated_at: new Date() },
      select: SELECT_CA,
    });
  }

  async eliminar(id: string, empresaId: string) {
    const ca = await this.getById(id, empresaId);

    if (ca.estado !== borrador) {
      throw new BadRequestException(
        'Solo se puede eliminar una compra asistida en estado borrador',
      );
    }

    await this.prisma.compra_asistida.delete({ where: { id } });
    return { ok: true };
  }

  async generarLink(id: string, empresaId: string) {
    const ca = await this.getById(id, empresaId);

    if (ca.estado !== cotizado) {
      throw new BadRequestException('Solo se puede generar link en estado cotizado');
    }

    if (ca.pago_bancard_id) {
      throw new BadRequestException('El pedido ya tiene un pago registrado');
    }

    const totalUsd = (ca.items || []).reduce(
      (s, i) => s + Number(i.cantidad) * Number(i.precio_usd),
      0,
    );
    const cotiz = Number(ca.cotizacion_usd_gs);
    const montoTotal =
      totalUsd * cotiz +
      Number(ca.servicio_gs) +
      Number(ca.logistica_gs) +
      Number(ca.comision_gs);

    if (montoTotal <= 0) {
      throw new BadRequestException('El monto total debe ser mayor a 0');
    }

    const link = await this.pagoLinkService.crear({
      empresaId,
      origenModulo: $Enums.bancard_origen_modulo.compra_asistida,
      origenId: id,
      montoTotal,
      moneda: 'PYG',
    });

    return {
      link_id: link.id,
      token: link.token,
      url: buildPagoLinkUrl(link.token),
      monto_total: montoTotal,
      fecha_expiracion: link.fecha_expiracion,
    };
  }

  private async generarNumero(empresaId: string): Promise<string> {
    const year = new Date().getFullYear();
    const count = await this.prisma.compra_asistida.count({
      where: {
        empresa_id: empresaId,
        numero: { startsWith: `CA-${year}-` },
      },
    });
    const seq = String(count + 1).padStart(6, '0');
    return `CA-${year}-${seq}`;
  }
}
