import { BadRequestException, Injectable, NotFoundException } from '@nestjs/common';
import { PrismaService } from '../prisma/prisma.service';
import { AgregarFacturasRutaDto, CreateRutaCobranzaDto, UpdateRutaEstadoDto } from './dto/rutas-cobranza.dto';

/**
 * RutasCobranzaService
 * ────────────────────────────────────────────────────────────────────────
 * Gestiona las rutas diarias de cobranza: planificación, ejecución y cierre.
 *
 * Casos de uso principales:
 *  1. Supervisor planifica ruta diaria → asigna facturas al cobrador
 *  2. Cobrador inicia la ruta → cambia estado a "en_curso"
 *  3. Cobrador completa visitas → se marcan facturas cobradas
 *  4. Supervisor/Cobrador cierra la ruta → estado "completada"
 *  5. Supervisor imprime hoja de ruta para cobrador de campo
 */
@Injectable()
export class RutasCobranzaService {
  constructor(private prisma: PrismaService) {}

  // ── Crear ruta de cobranza ───────────────────────────────────────────
  // Crea la ruta y opcionalmente asocia facturas asignadas al cobrador
  async create(empresaId: string, dto: CreateRutaCobranzaDto) {
    // Verificar que el cobrador existe y pertenece a la empresa
    const cobrador = await this.prisma.vendedores_cobradores.findFirst({
      where: { id: dto.cobrador_id, empresa_id: empresaId, active: true },
    });
    if (!cobrador) {
      throw new NotFoundException('Cobrador no encontrado o inactivo');
    }

    // Crear la ruta con sus facturas si se proporcionaron
    const ruta = await this.prisma.rutas_cobranza.create({
      data: {
        empresa_id: empresaId,
        cobrador_id: dto.cobrador_id,
        fecha: new Date(dto.fecha),
        observaciones: dto.observaciones,
        estado: 'planificada',
        total_facturas: dto.asignacion_factura_ids?.length || 0,
        ...(dto.asignacion_factura_ids?.length && {
          ruta_facturas: {
            create: dto.asignacion_factura_ids.map((id, index) => ({
              asignacion_factura_id: id,
              orden: index + 1,
            })),
          },
        }),
      },
      include: {
        cobrador: { select: { id: true, nombre: true, apellido: true } },
        ruta_facturas: {
          include: {
            asignacion_facturas: {
              include: {
                factura_cab: {
                  select: {
                    id: true,
                    dnumdoc: true,
                    total_factura: true,
                    saldo_pendiente: true,
                    clientes: {
                      select: {
                        id: true,
                        personas: { select: { razon_social: true, direccion: true } },
                      },
                    },
                  },
                },
              },
            },
          },
          orderBy: { orden: 'asc' },
        },
      },
    });

    return ruta;
  }

  // ── Listar rutas de cobranza ─────────────────────────────────────────
  // Filtra por cobrador, fecha, estado. Usado en panel de rendiciones.
  async findAll(
    empresaId: string,
    filters: {
      cobrador_id?: string;
      fecha_desde?: string;
      fecha_hasta?: string;
      estado?: string;
      page?: number;
      limit?: number;
    },
  ) {
    const { cobrador_id, fecha_desde, fecha_hasta, estado, page = 1, limit = 20 } = filters;

    const where: any = { empresa_id: empresaId };
    if (cobrador_id) where.cobrador_id = cobrador_id;
    if (estado) where.estado = estado;
    if (fecha_desde || fecha_hasta) {
      where.fecha = {};
      if (fecha_desde) where.fecha.gte = new Date(fecha_desde);
      if (fecha_hasta) where.fecha.lte = new Date(fecha_hasta);
    }

    const [data, total] = await Promise.all([
      this.prisma.rutas_cobranza.findMany({
        where,
        include: {
          cobrador: { select: { id: true, nombre: true, apellido: true } },
          _count: { select: { ruta_facturas: true } },
        },
        orderBy: { fecha: 'desc' },
        skip: (page - 1) * limit,
        take: limit,
      }),
      this.prisma.rutas_cobranza.count({ where }),
    ]);

    return { data, total, page, limit, totalPages: Math.ceil(total / limit) };
  }

  // ── Detalle de una ruta ──────────────────────────────────────────────
  // Incluye facturas con datos del cliente y montos para hoja de ruta
  async findOne(empresaId: string, id: string) {
    const ruta = await this.prisma.rutas_cobranza.findFirst({
      where: { id, empresa_id: empresaId },
      include: {
        cobrador: {
          select: { id: true, nombre: true, apellido: true, telefono: true, zona_id: true },
        },
        ruta_facturas: {
          include: {
            asignacion_facturas: {
              include: {
                factura_cab: {
                  select: {
                    id: true,
                    dnumdoc: true,
                    total_factura: true,
                    saldo_pendiente: true,
                    dfeemide: true,
                    clientes: {
                      select: {
                        id: true,
                        personas: {
                          select: { razon_social: true, ruc: true, direccion: true, celular: true },
                        },
                      },
                    },
                  },
                },
              },
            },
          },
          orderBy: { orden: 'asc' },
        },
        rendiciones_cobranza: {
          select: { id: true, estado: true, monto_cobrado: true, fecha_rendicion: true },
        },
      },
    });

    if (!ruta) {
      throw new NotFoundException('Ruta de cobranza no encontrada');
    }

    return ruta;
  }

  // ── Cambiar estado de la ruta ────────────────────────────────────────
  // Transiciones válidas: planificada→en_curso→completada, cualquiera→cancelada
  async updateEstado(empresaId: string, id: string, dto: UpdateRutaEstadoDto) {
    const ruta = await this.findOne(empresaId, id);

    const transicionesValidas: Record<string, string[]> = {
      planificada: ['en_curso', 'cancelada'],
      en_curso: ['completada', 'cancelada'],
      completada: [], // Estado final
      cancelada: [], // Estado final
    };

    const estadoActual = ruta.estado || 'planificada';
    const permitidos = transicionesValidas[estadoActual] || [];

    if (!permitidos.includes(dto.estado)) {
      throw new BadRequestException(
        `No se puede cambiar de "${estadoActual}" a "${dto.estado}". ` +
          `Transiciones válidas: ${permitidos.join(', ') || 'ninguna (estado final)'}`,
      );
    }

    return this.prisma.rutas_cobranza.update({
      where: { id },
      data: {
        estado: dto.estado,
        observaciones: dto.observaciones ?? ruta.observaciones,
        updated_at: new Date(),
      },
    });
  }

  // ── Agregar facturas a ruta existente ────────────────────────────────
  async agregarFacturas(empresaId: string, id: string, dto: AgregarFacturasRutaDto) {
    const ruta = await this.findOne(empresaId, id);

    if (ruta.estado !== 'planificada') {
      throw new BadRequestException('Solo se pueden agregar facturas a rutas en estado "planificada"');
    }

    // Obtener el orden máximo actual
    const maxOrden = ruta.ruta_facturas.length > 0 ? Math.max(...ruta.ruta_facturas.map((rf) => rf.orden || 0)) : 0;

    const ordenInicio = dto.orden_inicio ?? maxOrden + 1;

    // Crear las nuevas asignaciones
    await this.prisma.ruta_facturas.createMany({
      data: dto.asignacion_factura_ids.map((asigId, index) => ({
        ruta_id: id,
        asignacion_factura_id: asigId,
        orden: ordenInicio + index,
      })),
    });

    // Actualizar contador de facturas
    const totalFacturas = await this.prisma.ruta_facturas.count({
      where: { ruta_id: id },
    });

    await this.prisma.rutas_cobranza.update({
      where: { id },
      data: { total_facturas: totalFacturas, updated_at: new Date() },
    });

    return this.findOne(empresaId, id);
  }

  // ── Hoja de ruta para impresión ──────────────────────────────────────
  // Devuelve datos optimizados para generar PDF/imprimir la hoja de ruta
  async getHojaRuta(empresaId: string, id: string) {
    const ruta = await this.findOne(empresaId, id);

    // Calcular totales
    let totalPendiente = 0;
    const facturas = ruta.ruta_facturas.map((rf, index) => {
      const factura = rf.asignacion_facturas?.factura_cab;
      const saldo = Number(factura?.saldo_pendiente || 0);
      totalPendiente += saldo;

      return {
        orden: index + 1,
        numero_factura: factura?.dnumdoc,
        cliente: factura?.clientes?.personas?.razon_social,
        ruc: factura?.clientes?.personas?.ruc,
        direccion: factura?.clientes?.personas?.direccion,
        telefono: factura?.clientes?.personas?.celular,
        total_factura: factura?.total_factura,
        saldo_pendiente: saldo,
        cobrado: rf.cobrado,
        monto_cobrado: rf.monto_cobrado,
      };
    });

    return {
      ruta_id: ruta.id,
      fecha: ruta.fecha,
      cobrador: ruta.cobrador,
      estado: ruta.estado,
      observaciones: ruta.observaciones,
      total_facturas: facturas.length,
      total_pendiente_cobrar: totalPendiente,
      facturas,
    };
  }

  // ── Rutas disponibles para rendición ─────────────────────────────────
  // Devuelve rutas completadas o en_curso de un cobrador sin rendición asociada
  async getRutasParaRendicion(empresaId: string, cobradorId: string) {
    return this.prisma.rutas_cobranza.findMany({
      where: {
        empresa_id: empresaId,
        cobrador_id: cobradorId,
        estado: { in: ['en_curso', 'completada'] },
      },
      include: {
        _count: { select: { ruta_facturas: true } },
      },
      orderBy: { fecha: 'desc' },
    });
  }
}
