'use strict';

const logger = require('../../utils/logger');
const licenseEntitlementService = require('../../license/licenseEntitlementService');
const { getBillingPrintConfig } = require('./billing-print.config');
const ticketCapture = require('./billing-print.ticketCapture');
const evidence = require('./billing-print.evidence');
const printerAvailability = require('./billing-print.printerAvailability');
const cloudToken = require('./billing-print.cloudToken');

const NO_LICENSE_ACTION = 'Active o sincronice una licencia con facturacion/autofactura QR. El ticket base puede imprimirse si existe una impresora fisica configurada.';

function bool(value) {
  return value === true;
}

function firstString(...values) {
  for (const value of values) {
    if (value !== undefined && value !== null && String(value).trim() !== '') return String(value).trim();
  }
  return null;
}

function samePrinterName(left, right) {
  return String(left || '').trim().toLowerCase() === String(right || '').trim().toLowerCase();
}

function hasOwn(object, key) {
  return Object.prototype.hasOwnProperty.call(object || {}, key);
}

function tokenLooksValid(token) {
  if (!token) return false;
  try {
    cloudToken.validateTicketToken(token);
    return true;
  } catch (_) {
    return false;
  }
}

function basePrintBlockedCause({ configEnabled, outputPrinterName, outputPrinterIsCapturePrinter }) {
  if (!configEnabled) return 'CONFIG_DISABLED';
  if (!outputPrinterName) return 'OUTPUT_PRINTER_MISSING';
  if (outputPrinterIsCapturePrinter) return 'OUTPUT_PRINTER_IS_CAPTURE_PRINTER';
  return null;
}

function qrBlockedCause({ baseCause, qrEnabled, license, entitlements, tenant, billingCodeRequired, billingCode, ticketTokenRequired, ticketTokenValid }) {
  if (baseCause) return baseCause;
  if (qrEnabled === false) return 'BILLING_NOT_READY';
  if (!license || license.valid !== true) {
    if (license?.status === 'expired') return 'LICENSE_EXPIRED';
    if (license?.status === 'suspended') return 'LICENSE_SUSPENDED';
    if (license?.reason === 'LICENSE_STATE_MISSING') return 'PENDING_LICENSE';
    return 'PENDING_LICENSE';
  }
  if (!entitlements.billing || !entitlements.autofactura || !entitlements.billingQr) return 'NO_BILLING_ENTITLEMENT';
  // El QR abre el portal y no timbra por si mismo. PAC/CSD/readiness fiscal se
  // validan al solicitar el CFDI, no al imprimir el acceso al portal.
  if (!tenant.branchId && !tenant.branchCode) return 'BRANCH_NOT_CONFIGURED';
  if (billingCodeRequired && !billingCode) return 'BILLING_CODE_NOT_FOUND';
  if (ticketTokenRequired && !ticketTokenValid) return 'TOKEN_CREATION_FAILED';
  return null;
}

function qrMode(readiness) {
  const environment = String(readiness.billingEnvironment || readiness.pacEnvironment || '').toLowerCase();
  if ((environment === 'sandbox' || environment === 'assigned_test') && readiness.billingTestReady === true) return 'sandbox';
  if (environment === 'production' && readiness.productionBillingReady === true) return 'production';
  return 'portal';
}

function recommendedAction(cause) {
  if (!cause) return null;
  if (cause === 'PENDING_LICENSE' || cause === 'LICENSE_STATE_MISSING') return NO_LICENSE_ACTION;
  if (cause === 'LICENSE_EXPIRED') return 'Renueve o sincronice la licencia. El ticket base puede imprimirse sin QR.';
  if (cause === 'LICENSE_SUSPENDED') return 'Revise el estado de la licencia en ARKHON Cloud. El ticket base puede imprimirse sin QR.';
  if (cause === 'NO_BILLING_ENTITLEMENT') return 'Active entitlements de facturacion/autofactura QR para imprimir QR.';
  if (cause === 'BILLING_NOT_READY') return 'El QR esta disponible; complete PAC y CSD para habilitar el timbrado.';
  if (cause === 'BRANCH_NOT_CONFIGURED') return 'Vincule la PC a una sucursal Cloud con licencia de Facturacion.';
  if (cause === 'MICFDI_CODIGOSUCURSAL_PENDING') return 'Adjunte CODIGOSUCURSAL real de MICFDI y ejecute PAC validate antes de imprimir QR.';
  if (cause === 'MICFDI_CSD_NOT_ACTIVE') return 'Active los CSD del RFC emisor en MICFDI y vuelva a ejecutar PAC validate.';
  if (cause === 'PAC_VALIDATE_PENDING') return 'Ejecute PAC validate antes de imprimir QR.';
  if (cause === 'PAC_CONFIG_TEST_FAILED') return 'Revise la validacion PAC antes de imprimir QR.';
  if (cause === 'BILLING_CODE_NOT_FOUND') return 'El ticket base se imprimio sin QR porque no contiene codigo de facturacion.';
  if (cause === 'TOKEN_CREATION_FAILED') return 'Revise conectividad Cloud y emision de token tkt_*.';
  if (/OUTPUT_PRINTER/.test(cause)) return 'Configure una impresora POS fisica distinta de ARKHON Ticket Capture.';
  if (cause === 'CONFIG_DISABLED') return 'Active billing-print para procesar PRN.';
  return null;
}

function emitContextEvents(context) {
  const payload = {
    licenseStatus: context.license.status,
    licenseSource: context.license.source,
    licenseValid: context.license.valid,
    qrAllowed: context.decisions.qrAllowed,
    qrBlockedCause: context.decisions.qrBlockedCause,
    basePrintAllowed: context.decisions.basePrintAllowed,
    outputPrinterName: context.printing.outputPrinterName,
    branchCodePresent: Boolean(context.tenant.branchCode),
    branchSlugPresent: Boolean(context.tenant.branchSlug),
    controlNumberPresent: Boolean(context.tenant.controlNumber),
  };
  logger.info({ event: 'BILLING_PRINT_RUNTIME_CONTEXT_BUILT', ...payload });
  evidence.appendLog('ticket-capture.log', 'BILLING_PRINT_RUNTIME_CONTEXT_BUILT', payload);
  if (context.license.reason === 'LICENSE_STATE_MISSING') {
    logger.warn({ event: 'BILLING_PRINT_LICENSE_STATE_MISSING', source: context.license.source });
    evidence.appendLog('ticket-capture.log', 'BILLING_PRINT_LICENSE_STATE_MISSING', { source: context.license.source });
  }
  if (context.raw.entitlementPresent !== true || (!context.entitlements.billing && !context.entitlements.billingQr)) {
    evidence.appendLog('ticket-capture.log', 'BILLING_PRINT_ENTITLEMENT_MISSING', {
      licenseStatus: context.license.status,
      entitlementPresent: context.raw.entitlementPresent,
    });
  }
  if (!context.tenant.branchId && !context.tenant.branchCode) {
    evidence.appendLog('ticket-capture.log', 'BILLING_PRINT_BRANCH_NOT_CONFIGURED', {
      branchCodePresent: Boolean(context.tenant.branchCode),
      branchSlugPresent: Boolean(context.tenant.branchSlug),
    });
  }
}

function resolveBillingPrintRuntimeContext(input = {}) {
  const options = input === null ? { licenseState: null, cachedEntitlement: null } : input;
  const config = options.config || getBillingPrintConfig();
  const entitlement = licenseEntitlementService.resolveForBillingPrint({
    licenseState: hasOwn(options, 'licenseState') ? options.licenseState : undefined,
    entitlement: options.entitlement,
    entitlementSource: options.entitlementSource,
    cachedEntitlement: hasOwn(options, 'cachedEntitlement') ? options.cachedEntitlement : undefined,
  });
  const captureDiagnostic = options.captureDiagnostic || ticketCapture.inspectTicketCapture(config);
  const outputPrinterName = firstString(config.outputPrinterName, config.printerName);
  const capturePrinterName = firstString(config.capturePrinterName, ticketCapture.CAPTURE_PRINTER_NAME);
  const outputPrinterIsCapturePrinter = Boolean(outputPrinterName && samePrinterName(outputPrinterName, capturePrinterName));
  const outputPrinterAvailable = hasOwn(options, 'outputPrinterAvailable')
    ? options.outputPrinterAvailable
    : hasOwn(options, 'printerAvailable')
      ? options.printerAvailable
      : printerAvailability.isPrinterAvailable(outputPrinterName);
  const configEnabled = config.enabled === true;
  const captureEnabled = config.captureEnabled !== false;
  const capturePrinterReady = captureDiagnostic.capturePrinterReady === true;

  const tenant = {
    tenantId: firstString(entitlement.tenant.tenantId, config.tenantId),
    branchId: firstString(entitlement.tenant.branchId, config.branchId),
    controlNumber: firstString(entitlement.tenant.controlNumber, config.controlNumber),
    branchCode: firstString(entitlement.tenant.branchCode, config.branchCode),
    branchSlug: firstString(entitlement.tenant.branchSlug, config.branchSlug),
    branchName: firstString(entitlement.tenant.branchName, config.branchName),
    codigosucursal: firstString(entitlement.tenant.codigosucursal, config.codigosucursal),
  };
  const readiness = {
    portalEnabled: bool(entitlement.readiness.portalEnabled),
    pacConfigured: bool(entitlement.readiness.pacConfigured),
    csdConfigured: bool(entitlement.readiness.csdConfigured),
    billingConfigured: bool(entitlement.readiness.billingConfigured),
    billingEnvironment: firstString(entitlement.readiness.billingEnvironment, entitlement.readiness.billing_environment),
    pacEnvironment: firstString(entitlement.readiness.pacEnvironment, entitlement.readiness.pac_environment),
    billingTestReady: bool(entitlement.readiness.billingTestReady || entitlement.readiness.billing_test_ready),
    productionBillingReady: bool(entitlement.readiness.productionBillingReady || entitlement.readiness.production_billing_ready),
    reason: firstString(entitlement.readiness.reason, entitlement.readiness.readinessReason, entitlement.readiness.error),
  };
  const baseCause = basePrintBlockedCause({
    configEnabled,
    outputPrinterName,
    outputPrinterIsCapturePrinter,
  });
  const billingCodeRequired = hasOwn(options, 'billingCode');
  const billingCode = firstString(options.billingCode);
  const ticketTokenRequired = hasOwn(options, 'ticketToken');
  const ticketTokenValid = ticketTokenRequired ? tokenLooksValid(options.ticketToken) : true;
  const qrCause = qrBlockedCause({
    baseCause,
    qrEnabled: config.qrEnabled !== false,
    license: entitlement.license,
    entitlements: entitlement.entitlements,
    tenant,
    billingCodeRequired,
    billingCode,
    ticketTokenRequired,
    ticketTokenValid,
  });
  const basePrintAllowed = !baseCause;
  const qrAllowed = !qrCause;
  const resolvedQrMode = qrAllowed ? qrMode(readiness) : null;

  const context = {
    license: {
      status: entitlement.license.status,
      valid: entitlement.license.valid === true,
      planType: entitlement.license.planType || null,
      validFrom: entitlement.license.validFrom || null,
      validUntil: entitlement.license.validUntil || null,
      graceUntil: entitlement.license.graceUntil || null,
      source: entitlement.license.source || 'missing',
      reason: entitlement.license.reason || null,
    },
    tenant,
    entitlements: { ...entitlement.entitlements },
    readiness,
    printing: {
      configEnabled,
      captureEnabled,
      capturePrinterReady,
      outputPrinterName,
      outputPrinterAvailable,
      outputPrinterIsCapturePrinter,
      canCapture: configEnabled && captureEnabled && capturePrinterReady,
      canPrintBaseTicket: basePrintAllowed,
      canRequestToken: qrAllowed,
      canPrintQr: qrAllowed,
      canPrintQrPortal: qrAllowed && resolvedQrMode === 'portal',
      canPrintQrSandbox: qrAllowed && resolvedQrMode === 'sandbox',
      canPrintQrProduction: qrAllowed && resolvedQrMode === 'production',
    },
    decisions: {
      qrAllowed,
      qrBlockedCause: qrCause,
      basePrintAllowed,
      basePrintBlockedCause: baseCause,
      recommendedAction: recommendedAction(qrCause || baseCause),
    },
    raw: {
      licensePresent: entitlement.raw.licensePresent === true,
      entitlementPresent: entitlement.raw.entitlementPresent === true,
      cloudReadinessPresent: entitlement.raw.cloudReadinessPresent === true,
    },
  };

  if (options.silent !== true) emitContextEvents(context);
  return context;
}

module.exports = {
  resolveBillingPrintRuntimeContext,
  NO_LICENSE_ACTION,
};
