import { BadRequestException, Injectable, Logger } from "@nestjs/common";
import { Repository } from "typeorm";
import { TenantRepositoryAccessor } from "../tenancy/tenant-repository.accessor";
import { MaepluMirrorService } from "../productos/maeplu-mirror.service";
import { TallerParametrosService } from "../taller-parametros/taller-parametros.service";
import { FacturacionPosConfig, PostouchClientService } from "./postouch-client.service";
import { OtFacturacion } from "./entities/ot-facturacion.entity";

export type OtParaFacturar = {
  id: number;
  numeroOt: string;
  // Una tienda especifica, nunca "el tenant" en general -- computadora en
  // POSTouch resuelve a UNA tienda/caja puntual, asi que sin tiendaId no hay
  // forma segura de saber a cual enviar la orden.
  tiendaId: number | null;
  clienteTarjeta: string;
  cliente: {
    nombre: string;
    nit: string;
    telefono?: string;
    email?: string;
    direccion?: string;
  };
  cotizacion: {
    lineas: Array<{
      plu: string;
      descripcion: string;
      precioUnitario: number;
      cantidad: number;
      esServicio: boolean;
      origenLocal?: boolean;
    }>;
  };
};

export type ResultadoEnvioPos =
  | { exito: true }
  | { exito: false; error: string };

@Injectable()
export class FacturacionPosService {
  private readonly logger = new Logger(FacturacionPosService.name);

  constructor(
    private readonly tenantAccessor: TenantRepositoryAccessor,
    private readonly maepluMirrorService: MaepluMirrorService,
    private readonly postouchClientService: PostouchClientService,
    private readonly tallerParametros: TallerParametrosService,
  ) {}

  private get otFacturacionRepository(): Repository<OtFacturacion> {
    return this.tenantAccessor.repositoryFor(OtFacturacion);
  }

  // Por TIENDA, no por tenant -- ver comentario en TallerParametro entity /
  // spec §3.3 sobre por que facturacion_pos vive con ambito='tienda'. Ya no
  // lee columnas de maetie directo: taller_parametros es la fuente de verdad
  // desde este cambio (Task 1 migro los datos existentes).
  private async getConfigForTienda(tiendaId: number): Promise<FacturacionPosConfig | null> {
    const habilitada = await this.tallerParametros.getParaTienda(tiendaId, "facturacion_pos.habilitada");
    if (!habilitada) {
      return null;
    }

    const url = await this.tallerParametros.getParaTienda(tiendaId, "facturacion_pos.url");
    const username = await this.tallerParametros.getParaTienda(tiendaId, "facturacion_pos.username");
    const password = await this.tallerParametros.getParaTienda(tiendaId, "facturacion_pos.password");
    const computadora = await this.tallerParametros.getParaTienda(tiendaId, "facturacion_pos.computadora");
    const precioDecimales = await this.tallerParametros.getParaTienda(tiendaId, "facturacion_pos.precio_decimales");

    if (!url) {
      throw new BadRequestException(
        `La tienda ${tiendaId} tiene facturacionPosHabilitada pero no tiene facturacionPosUrl configurada`,
      );
    }
    if (!username) {
      throw new BadRequestException(
        `La tienda ${tiendaId} tiene facturacionPosHabilitada pero no tiene facturacionPosUsername configurado`,
      );
    }
    if (!password) {
      throw new BadRequestException(
        `La tienda ${tiendaId} tiene facturacionPosHabilitada pero no tiene contraseña configurada`,
      );
    }
    if (!computadora) {
      throw new BadRequestException(
        `La tienda ${tiendaId} tiene facturacionPosHabilitada pero no tiene facturacionPosComputadora configurada`,
      );
    }
    if (!precioDecimales && precioDecimales !== 0) {
      throw new BadRequestException(
        `La tienda ${tiendaId} tiene facturacionPosHabilitada pero no tiene facturacionPosPrecioDecimales configurado`,
      );
    }

    return { habilitada: true, url, username, password, computadora, precioDecimales };
  }

  async enviarOrdenTerminada(ot: OtParaFacturar): Promise<ResultadoEnvioPos> {
    try {
      const usaFacturacion = await this.tallerParametros.getBooleanConFallback("facturacion.usa_facturacion");
      if (!usaFacturacion) {
        return {
          exito: false,
          error: "Facturación automática desactivada para este tenant -- Configuración → Facturación.",
        };
      }

      if (ot.tiendaId === null) {
        return { exito: false, error: "OT sin tienda asignada, no se puede facturar por POS" };
      }

      const config = await this.getConfigForTienda(ot.tiendaId);

      if (!config) {
        return { exito: false, error: "Facturacion POS no habilitada para esta tienda" };
      }

      for (const linea of ot.cotizacion.lineas) {
        if (linea.origenLocal) {
          await this.maepluMirrorService.insertIfMissing({
            plu: linea.plu,
            desclarga: linea.descripcion,
            precio: linea.precioUnitario,
            costo: 0,
            iva: 0,
            pagaiva: true,
            esServicio: linea.esServicio,
            usainventario: false,
          });
        }
      }

      const factura = await this.postouchClientService.enviarOrden(config, {
        id: ot.id,
        cliente: {
          tarjeta: ot.clienteTarjeta,
          nombre: ot.cliente.nombre,
          nit: ot.cliente.nit,
          telefono: ot.cliente.telefono,
          email: ot.cliente.email,
          direccion: ot.cliente.direccion,
        },
        lineas: ot.cotizacion.lineas.map((linea) => ({
          plu: linea.plu,
          cantidad: linea.cantidad,
          precio: linea.precioUnitario,
        })),
      });

      await this.otFacturacionRepository.save({
        otId: ot.id,
        tienda: factura.tienda,
        caja: factura.caja,
        tipo: factura.tipo,
        serie: factura.serie,
        transac: factura.transac,
      });

      return { exito: true };
    } catch (error: any) {
      this.logger.error(
        `Error enviando OT ${ot.numeroOt} a POSTouch: ${error?.message ?? error}`,
      );
      return { exito: false, error: error?.message ?? "Error desconocido enviando a POSTouch" };
    }
  }
}
