import { BadRequestException, Injectable, Logger } from "@nestjs/common";

// Config resuelta para UNA tienda especifica -- ver comentario en
// TenantConfig entity / Tienda entity sobre por que esto es por tienda y no
// por tenant.
export type FacturacionPosConfig = {
  habilitada: boolean;
  url: string;
  username: string;
  password: string;
  computadora: string;
  precioDecimales: number;
};

export type OrdenPosLinea = {
  plu: string;
  cantidad: number;
  precio: number;
};

export type OrdenPosCliente = {
  tarjeta?: string;
  nombre: string;
  nit: string;
  telefono?: string;
  email?: string;
  direccion?: string;
};

export type OrdenPosInput = {
  // Debe ser numerico: POSTouch persiste este valor en PedidosBitacora.webificaOrderId,
  // columna `float` (PedidosBitacora.php:277) — un string (p.ej. "OT-20260909-001")
  // se trunca a 0 y colisiona con cualquier otra orden.
  id: number;
  cliente: OrdenPosCliente;
  lineas: OrdenPosLinea[];
};

export type OrdenPosCreada = {
  tienda: number;
  caja: number;
  tipo: string;
  serie: string;
  transac: number;
};

// Redondeo a los decimales exactos configurados para la tienda destino
// (ParametrosGlobales.getPrecioDecimales() en POSTouch) — la tolerancia de
// esa validacion es de solo Q0.005 por linea (Transaccion.php:1955-1977),
// asi que un redondeo distinto al de la tienda hace que se rechace la
// orden completa antes de facturar.
export function redondear(valor: number, decimales: number): number {
  const factor = 10 ** decimales;
  return Math.round(Number((valor * factor).toPrecision(15))) / factor;
}

const MEDIA_POR_LIQUIDAR = 99;

export type CredencialesPosParaProbar = Pick<FacturacionPosConfig, "url" | "username" | "password" | "computadora">;

export type ResultadoProbarConexion = { ok: true } | { ok: false; detalle: string };

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

  async enviarOrden(
    config: FacturacionPosConfig,
    orden: OrdenPosInput,
  ): Promise<OrdenPosCreada> {
    const productos = orden.lineas.map((linea) => {
      const precio = redondear(linea.precio, config.precioDecimales);
      const monto = redondear(precio * linea.cantidad, config.precioDecimales);
      return {
        plu: linea.plu,
        cantidad: linea.cantidad,
        precio,
        monto,
        descuentoItemPorcentaje: 0,
        comboNumero: 0,
        pluPadre: "",
      };
    });

    const total = redondear(
      productos.reduce((sum, p) => sum + p.monto, 0),
      config.precioDecimales,
    );

    const payload = {
      _username: config.username,
      _password: config.password,
      computadora: config.computadora,
      ordenes: [
        {
          id: orden.id,
          clienteTarjeta: orden.cliente.tarjeta ?? "",
          clienteNombre: orden.cliente.nombre,
          clienteTelefono: orden.cliente.telefono ?? "",
          clienteEmail: orden.cliente.email ?? "",
          clienteNIT: orden.cliente.nit,
          clienteDireccion2: orden.cliente.direccion ?? "",
          clienteCiudad: "",
          clienteDepartamen: "0",
          formasPago: [
            { media: MEDIA_POR_LIQUIDAR, emisor: 0, codigo: 0, monto: total },
          ],
          productos,
        },
      ],
    };

    const response = await fetch(config.url, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify(payload),
    });

    const text = await response.text();
    let data: any = {};
    try {
      data = text ? JSON.parse(text) : {};
    } catch {
      data = {};
    }

    if (!response.ok) {
      throw new BadRequestException(
        `POSTouch respondio con error al crear la orden: ${text.slice(0, 300)}`,
      );
    }

    if (data?.error) {
      throw new BadRequestException(`POSTouch rechazo la orden: ${data.error}`);
    }

    const factura = data?.facturas?.[0];
    if (!factura) {
      throw new BadRequestException(
        `POSTouch no devolvio la factura creada: ${text.slice(0, 300)}`,
      );
    }

    return {
      tienda: factura.tienda,
      caja: factura.caja,
      tipo: factura.tipo,
      serie: factura.serie,
      transac: factura.transac,
    };
  }

  // Sonda segura por construccion: ordenes:[] verificado contra el codigo
  // real de POSTouch (DomicilioController::createOrdenesEdngtAction llama
  // login() ANTES de tocar `ordenes`; con `ordenes:[]` el foreach de
  // Domicilio::createOrdenesEdngt no corre ni una vez -- cero PLUs, cero
  // clientes, cero facturas). Independiente de enviarOrden: nunca cae a el
  // bajo ninguna rama de error, nunca asume exito ante una respuesta que no
  // coincide exactamente con la esperada.
  async probarConexion(config: CredencialesPosParaProbar): Promise<ResultadoProbarConexion> {
    const payload = {
      _username: config.username,
      _password: config.password,
      computadora: config.computadora,
      ordenes: [] as unknown[],
    };

    let response: Response;
    try {
      response = await fetch(config.url, {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify(payload),
      });
    } catch (error: any) {
      this.logDiagnosticoProbarConexion(payload, null, `Error de red: ${error?.message ?? error}`);
      return { ok: false, detalle: `No se pudo contactar POSTouch: ${error?.message ?? "error de red"}` };
    }

    const text = await response.text();
    let data: any;
    try {
      data = text ? JSON.parse(text) : {};
    } catch {
      this.logDiagnosticoProbarConexion(payload, text, "Respuesta no es JSON valido");
      return { ok: false, detalle: "No se pudo verificar la respuesta de POSTouch" };
    }

    if (!response.ok) {
      this.logDiagnosticoProbarConexion(payload, text, `HTTP ${response.status}`);
      return { ok: false, detalle: `POSTouch respondió con error: ${text.slice(0, 300)}` };
    }

    if (data?.error) {
      this.logDiagnosticoProbarConexion(payload, text, "data.error presente");
      return { ok: false, detalle: `POSTouch rechazó la conexión: ${data.error}` };
    }

    const esRespuestaEsperada =
      data && typeof data === "object" && data.procesadas === 0 && Array.isArray(data.facturas) && data.facturas.length === 0;

    if (!esRespuestaEsperada) {
      this.logDiagnosticoProbarConexion(payload, text, "Forma de respuesta inesperada");
      return { ok: false, detalle: "No se pudo verificar la respuesta de POSTouch" };
    }

    this.logDiagnosticoProbarConexion(payload, text, "ok");
    return { ok: true };
  }

  private logDiagnosticoProbarConexion(
    payload: Record<string, unknown>,
    respuesta: string | null,
    resultado: string,
  ): void {
    const payloadSeguro = { ...payload, _password: "***" };
    this.logger.debug(
      `[probarConexion] payload=${JSON.stringify(payloadSeguro)} respuesta=${(respuesta ?? "").slice(0, 300)} resultado=${resultado}`,
    );
  }
}
