import { Injectable } from '@nestjs/common';
import { PrismaService } from 'src/prisma/prisma.service';
import { PersonasService } from 'src/personas/personas.service';

/**
 * Datos normalizados que necesita la validación, independientes de si vienen
 * del DTO de creación o de una remisión ya persistida.
 */
interface ContextoValidacion {
  empresaId: string;
  clientePersonaId: string | null;
  clientePersonaDireccion: string | null;
  tipoOperacionCodigo: number | null;
  transportistaPersona: any | null;
  choferPersona: any | null;
  agenteRequerido: boolean;
  agentePersona: any | null;
  vehiculo: any | null;
  motivoCodigo: number | null;
  motivoSeleccionado: boolean;
  descripcionMotivo: string | null;
  direccionSalida: string | null;
  direccionEntrega: string | null;
  kilometros: number | null;
}

/**
 * Validación de negocio de la Nota de Remisión. Acumula TODOS los errores (no
 * corta en el primero) para que el usuario los corrija de una sola vez, con
 * prefijo por entidad.
 *
 * - `validarCreacion(cabecera)` valida ANTES de persistir (flujo create, igual
 *   que factura/NC: si hay errores no se crea nada y vuelven al frontend).
 * - `validar(remisionId)` valida una remisión ya persistida (reenvío manual).
 *
 * Diseñado para crecer de a poco: las reglas viven en los métodos `validarX`.
 * La validación del cliente reutiliza `PersonasService.validatePersonaById`
 * (mismas reglas que factura/NC).
 */
@Injectable()
export class NotaRemisionValidacionService {
  constructor(
    private readonly prisma: PrismaService,
    private readonly personasService: PersonasService,
  ) {}

  /** Valida a partir del DTO de creación (antes de persistir). */
  async validarCreacion(cabecera: any, empresaId: string): Promise<string[]> {
    const [cliente, transportista, chofer, vehiculo, agente, motivo] = await Promise.all([
      this.prisma.clientes.findFirst({
        where: { id: cabecera.cliente_id, personas: { empresa_id: empresaId } },
        include: { personas: true, tipo_operacion: true },
      }),
      this.prisma.transportistas.findFirst({
        where: { id: cabecera.transportista_id, empresa_id: empresaId },
        include: { personas: { include: { naturaleza_receptor: true, tipo_documento_identidad: true } } },
      }),
      this.prisma.choferes.findFirst({
        where: { id: cabecera.chofer_id, empresa_id: empresaId },
        include: { personas: true },
      }),
      this.prisma.vehiculos.findFirst({ where: { id: cabecera.vehiculo_id, empresa_id: empresaId } }),
      cabecera.agente_id
        ? this.prisma.agentes_transporte.findFirst({
            where: { id: cabecera.agente_id, empresa_id: empresaId },
            include: { personas: true },
          })
        : Promise.resolve(null),
      cabecera.motivo_emision_id
        ? this.prisma.motivo_emision_remision.findUnique({ where: { id: cabecera.motivo_emision_id } })
        : Promise.resolve(null),
    ]);

    return this.ejecutar({
      empresaId,
      clientePersonaId: cliente?.personas?.id ?? null,
      clientePersonaDireccion: cliente?.personas?.direccion ?? null,
      tipoOperacionCodigo: cliente?.tipo_operacion?.codigo ?? null,
      transportistaPersona: transportista?.personas ?? null,
      choferPersona: chofer?.personas ?? null,
      agenteRequerido: !!cabecera.agente_id,
      agentePersona: agente?.personas ?? null,
      vehiculo: vehiculo ?? null,
      motivoCodigo: motivo?.codigo ?? null,
      motivoSeleccionado: !!cabecera.motivo_emision_id && !!motivo,
      descripcionMotivo: cabecera.otro_motivo ?? cabecera.info_adicional ?? null,
      direccionSalida: cabecera.direccion_salida ?? null,
      direccionEntrega: cabecera.direccion_entrega ?? null,
      kilometros: cabecera.kilometros != null ? Number(cabecera.kilometros) : null,
    });
  }

  /** Valida una remisión ya persistida (reenvío manual a SIFEN). */
  async validar(remisionId: string, empresaId: string): Promise<string[]> {
    const rem = await this.prisma.nota_remision_cab.findFirst({
      where: { id: remisionId, empresa_id: empresaId },
      include: {
        clientes: { include: { personas: true, tipo_operacion: true } },
        transportistas: {
          include: { personas: { include: { naturaleza_receptor: true, tipo_documento_identidad: true } } },
        },
        choferes: { include: { personas: true } },
        agentes_transporte: { include: { personas: true } },
        vehiculos: true,
        motivo_emision_remision: true,
      },
    });
    if (!rem) return ['La nota de remisión no existe'];

    return this.ejecutar({
      empresaId,
      clientePersonaId: rem.clientes?.personas?.id ?? null,
      clientePersonaDireccion: rem.clientes?.personas?.direccion ?? null,
      tipoOperacionCodigo: (rem.clientes as any)?.tipo_operacion?.codigo ?? null,
      transportistaPersona: rem.transportistas?.personas ?? null,
      choferPersona: rem.choferes?.personas ?? null,
      agenteRequerido: !!rem.agente_id,
      agentePersona: rem.agentes_transporte?.personas ?? null,
      vehiculo: rem.vehiculos ?? null,
      motivoCodigo: rem.motivo_emision_remision?.codigo ?? null,
      motivoSeleccionado: !!rem.motivo_emision_id && !!rem.motivo_emision_remision,
      descripcionMotivo: rem.dinfadic ?? null,
      direccionSalida: rem.ddirlocsal ?? null,
      direccionEntrega: rem.ddirlocent ?? null,
      kilometros: rem.dkmr != null ? Number(rem.dkmr) : null,
    });
  }

  // ── Orquestación ────────────────────────────────────────────────────
  private async ejecutar(ctx: ContextoValidacion): Promise<string[]> {
    const errores: string[] = [];
    await this.validarCliente(ctx, errores);
    this.validarDatosRemision(ctx, errores);
    this.validarVehiculo(ctx, errores);
    this.validarTransportista(ctx, errores);
    this.validarChofer(ctx, errores);
    this.validarAgente(ctx, errores);
    return errores;
  }

  // ── Cliente / receptor ──────────────────────────────────────────────
  // Reutiliza la validación completa de persona (RUC/DV/tipo contribuyente,
  // naturaleza, documento, geo, etc.) que ya usan factura y NC.
  private async validarCliente(ctx: ContextoValidacion, errores: string[]): Promise<void> {
    if (!ctx.clientePersonaId) {
      errores.push('Cliente: no tiene una persona asociada');
      return;
    }
    const errs = await this.personasService.validatePersonaById(ctx.clientePersonaId, ctx.empresaId, {
      tipoOperacionCodigo: ctx.tipoOperacionCodigo,
      // La remisión opera solo dentro de Paraguay (cPaisRec fijo en 'PRY'),
      // así que no se exige país en la ficha del cliente.
      omitirValidacionPais: true,
    });
    errs.forEach((e) => errores.push(`Cliente: ${e}`));

    // dDirRec es obligatorio en remisión. Si la persona no tiene dirección,
    // se admite la dirección de entrega cargada en la remisión.
    const tieneDireccion =
      (ctx.clientePersonaDireccion?.trim() || '') !== '' || (ctx.direccionEntrega?.trim() || '') !== '';
    if (!tieneDireccion) {
      errores.push('Cliente: la dirección es obligatoria (cargue la del cliente o complete la dirección de entrega)');
    }
  }

  // ── Datos propios de la remisión ────────────────────────────────────
  private validarDatosRemision(ctx: ContextoValidacion, errores: string[]): void {
    if (!ctx.motivoSeleccionado) {
      errores.push('Debe seleccionar el motivo de emisión');
    } else if (ctx.motivoCodigo === 99 && !(ctx.descripcionMotivo?.trim())) {
      errores.push('Motivo "Otro" (99): debe describir el motivo');
    }
    if (!(ctx.direccionSalida?.trim())) errores.push('Falta la dirección de salida');
    if (!(ctx.direccionEntrega?.trim())) errores.push('Falta la dirección de entrega');
    if (ctx.kilometros == null || ctx.kilometros <= 0) errores.push('Los kilómetros del recorrido deben ser mayores a 0');
  }

  // ── Vehículo ────────────────────────────────────────────────────────
  // Convención de la app: tipo_identificacion 1 = Matrícula (requiere nro_matricula),
  // 2 = Identificación (requiere nro_identificacion).
  private validarVehiculo(ctx: ContextoValidacion, errores: string[]): void {
    const v = ctx.vehiculo;
    if (!v) {
      errores.push('Vehículo: no seleccionado');
      return;
    }
    if (!(v.marca?.trim())) errores.push('Vehículo: la marca es obligatoria');
    if (v.tipo_identificacion == null) {
      errores.push('Vehículo: debe indicar el tipo de identificación (matrícula o nro. de identificación)');
    } else if (v.tipo_identificacion === 1 && !(v.nro_matricula?.trim())) {
      errores.push('Vehículo: el número de matrícula es obligatorio');
    } else if (v.tipo_identificacion === 2 && !(v.nro_identificacion?.trim())) {
      errores.push('Vehículo: el número de identificación es obligatorio');
    }
    // Límites SIFEN: matrícula hasta 7, identificación hasta 20.
    if ((v.nro_matricula?.trim()?.length ?? 0) > 7) {
      errores.push('Vehículo: el número de matrícula no puede superar los 7 caracteres');
    }
    if ((v.nro_identificacion?.trim()?.length ?? 0) > 20) {
      errores.push('Vehículo: el número de identificación no puede superar los 20 caracteres');
    }
  }

  // ── Transportista ───────────────────────────────────────────────────
  // SIFEN iNatTrans: 1 = contribuyente → RUC + DV; 2 = no contribuyente →
  // tipo y número de documento de identidad.
  private validarTransportista(ctx: ContextoValidacion, errores: string[]): void {
    const p = ctx.transportistaPersona;
    if (!p) {
      errores.push('Transportista: no seleccionado');
      return;
    }
    if (!(p.razon_social?.trim())) errores.push('Transportista: la razón social es obligatoria');
    // dDomFisc: domicilio fiscal del transportista, obligatorio en SIFEN.
    if (!(p.direccion?.trim())) errores.push('Transportista: el domicilio fiscal (dirección) es obligatorio');

    // El transportista debe ser contribuyente (con RUC + DV).
    const naturaleza = p.naturaleza_receptor?.codigo ?? null;
    if (naturaleza !== 1) {
      errores.push('Transportista: debe ser contribuyente (con RUC).');
    } else {
      const ruc = (p.ruc ?? '').trim();
      if (!ruc) {
        errores.push('Transportista: el RUC es obligatorio');
      } else if (!/^\d{3,8}$/.test(ruc)) {
        errores.push('Transportista: el RUC debe tener entre 3 y 8 dígitos');
      }
      const dv = p.dv == null ? '' : String(p.dv).trim();
      if (dv === '') {
        errores.push('Transportista: el dígito verificador (DV) es obligatorio');
      } else if (!/^\d$/.test(dv)) {
        errores.push('Transportista: el DV debe ser un número entre 0 y 9');
      }
    }
  }

  // ── Chofer ──────────────────────────────────────────────────────────
  private validarChofer(ctx: ContextoValidacion, errores: string[]): void {
    const p = ctx.choferPersona;
    if (!p) {
      errores.push('Chofer: no seleccionado');
      return;
    }
    if (!(p.razon_social?.trim())) errores.push('Chofer: el nombre es obligatorio');
    if (!(p.nro_documento?.trim())) errores.push('Chofer: el número de documento es obligatorio');
    // dDirChof: dirección del chofer, obligatoria en SIFEN.
    if (!(p.direccion?.trim())) errores.push('Chofer: la dirección es obligatoria');
  }

  // ── Agente de transporte (opcional) ─────────────────────────────────
  private validarAgente(ctx: ContextoValidacion, errores: string[]): void {
    if (!ctx.agenteRequerido) return; // el agente es opcional
    const p = ctx.agentePersona;
    if (!p) {
      errores.push('Agente de transporte: no tiene persona asociada');
      return;
    }
    if (!(p.razon_social?.trim())) errores.push('Agente de transporte: la razón social es obligatoria');
  }
}
