'use strict';

const fs = require('fs');
const os = require('os');
const path = require('path');
const { saveCredentials } = require('../utils/credentialStore');
const { authHeaders } = require('./agentAuth');
const { getHardwareId } = require('../license/hardwareId');
const entitlementCache = require('../license/entitlementCache');

const DEFAULT_CLOUD_URL = 'https://cloud.arkhonmonitor.com';

function mask(value) {
  const text = String(value || '');
  if (!text) return null;
  if (text.length <= 8) return '****';
  return `${text.slice(0, 4)}****${text.slice(-4)}`;
}

function readJson(filePath, fallback = {}) {
  try {
    return JSON.parse(fs.readFileSync(filePath, 'utf8'));
  } catch (_) {
    return fallback;
  }
}

function writeJson(filePath, value) {
  fs.mkdirSync(path.dirname(filePath), { recursive: true });
  fs.writeFileSync(filePath, `${JSON.stringify(value, null, 2)}\n`, 'utf8');
}

function normalizeCloudUrl(value) {
  return String(value || DEFAULT_CLOUD_URL).trim().replace(/\/+$/, '') || DEFAULT_CLOUD_URL;
}

const PROVISIONING_ERROR_MESSAGES = {
  CLAIM_CODE_INVALID: 'No se pudo validar el codigo de instalacion.',
  CLAIM_CODE_EXPIRED: 'El codigo de instalacion vencio. Genera uno nuevo desde Cloud.',
  CLAIM_CODE_ALREADY_USED: 'Este codigo ya fue utilizado.',
  LICENSE_NOT_ACTIVE: 'La licencia de esta sucursal no esta activa.',
  BRANCH_NOT_FOUND: 'No se encontro la sucursal asociada.',
  TENANT_NOT_FOUND: 'No se encontro la empresa asociada.',
  NETWORK_ERROR: 'No se pudo conectar con ARKHON Cloud.',
  SERVER_ERROR: 'No se pudo completar la activacion. Intenta nuevamente.',
};

function normalizeProvisioningErrorCode(value, status) {
  const normalized = String(value || '').trim().toUpperCase();
  const map = {
    INVALID_CLAIM_CODE: 'CLAIM_CODE_INVALID',
    INVALID_INSTALL_CODE: 'CLAIM_CODE_INVALID',
    INVALID_CODE: 'CLAIM_CODE_INVALID',
    CLAIM_INVALID: 'CLAIM_CODE_INVALID',
    CLAIM_CODE_REVOKED: 'CLAIM_CODE_INVALID',
    REVOKED: 'CLAIM_CODE_INVALID',
    EXPIRED: 'CLAIM_CODE_EXPIRED',
    CLAIM_EXPIRED: 'CLAIM_CODE_EXPIRED',
    USED: 'CLAIM_CODE_ALREADY_USED',
    ALREADY_USED: 'CLAIM_CODE_ALREADY_USED',
    INACTIVE_LICENSE: 'LICENSE_NOT_ACTIVE',
    LICENSE_INACTIVE: 'LICENSE_NOT_ACTIVE',
    TENANT_MISSING: 'TENANT_NOT_FOUND',
    BRANCH_MISSING: 'BRANCH_NOT_FOUND',
  };
  if (map[normalized]) return map[normalized];
  if (PROVISIONING_ERROR_MESSAGES[normalized]) return normalized;
  if (status >= 500) return 'SERVER_ERROR';
  return normalized || 'SERVER_ERROR';
}

function buildProvisioningError(payload = {}, status) {
  const rawCode = payload.code || payload.reason || payload.error;
  const code = normalizeProvisioningErrorCode(rawCode, status);
  return {
    code,
    message: PROVISIONING_ERROR_MESSAGES[code] || payload.message || payload.error || PROVISIONING_ERROR_MESSAGES.SERVER_ERROR,
  };
}

function normalizeBootstrapResponse(payload) {
  const tenantId = payload.tenantId || payload.tenant_id || payload.tenant?.id || '';
  const branchId = payload.branchId || payload.branch_id || payload.branch?.id || '';
  const branchCode = payload.branchCode || payload.branch_code || payload.branch?.code || payload.branch?.name || '';
  const branchSlug = payload.branchSlug || payload.branch_slug || payload.branch?.slug || '';
  const branchName = payload.branchName || payload.branch_name || payload.branch?.name || '';
  const agentId = payload.agentId || payload.agent_id || payload.connector?.id || payload.connector_id || '';
  const agentKeyId = payload.agentKeyId || payload.agent_key_id || payload.connector_key_id || '';
  const agentSecret = payload.agentSecret || payload.agent_secret || payload.connector_secret || '';
  const cloudUrl = normalizeCloudUrl(payload.cloudUrl || payload.cloud_url || payload.cloudBaseUrl);
  if (!tenantId || !branchId || !agentId || !agentKeyId || !agentSecret) {
    const missing = [];
    if (!tenantId) missing.push('tenantId');
    if (!branchId) missing.push('branchId');
    if (!agentId) missing.push('agentId');
    if (!agentKeyId) missing.push('agentKeyId');
    if (!agentSecret) missing.push('agentSecret');
    const err = new Error(`provisioning_response_incomplete: ${missing.join(',')}`);
    err.code = 'provisioning_response_incomplete';
    err.missing = missing;
    throw err;
  }
  return { tenantId, branchId, branchCode, branchSlug, branchName, agentId, agentKeyId, agentSecret, cloudUrl };
}

function asObject(value) {
  return value && typeof value === 'object' && !Array.isArray(value) ? value : {};
}

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

function normalizeLicenseIdentity(payload = {}) {
  const tenant = asObject(payload.tenant);
  const branch = asObject(payload.branch);
  const billing = asObject(payload.billing || payload.branchBilling || payload.mefacturo || payload.readiness);
  return {
    tenantId: firstString(tenant.id, payload.tenantId, payload.tenant_id, billing.tenantId, billing.tenant_id),
    branchId: firstString(branch.id, payload.branchId, payload.branch_id, billing.branchId, billing.branch_id),
    branchCode: firstString(branch.code, payload.branchCode, payload.branch_code, billing.branchCode, billing.branch_code),
    branchSlug: firstString(branch.slug, payload.branchSlug, payload.branch_slug, billing.branchSlug, billing.slug, billing.sucursalSlug),
    branchName: firstString(branch.name, payload.branchName, payload.branch_name, billing.branchName, billing.branch_name),
  };
}

function buildLocalConfig(current, resolved, input) {
  const now = new Date().toISOString();
  const features = {
    batch_sync: true,
    multibranch_sync: true,
    softrestaurant_write_enabled: false,
    ...(current.features || {}),
    ...(resolved.features || {}),
  };
  return {
    ...current,
    cloud_url: resolved.cloudUrl,
    cloudUrl: resolved.cloudUrl,
    tenant_id: resolved.tenantId,
    tenantId: resolved.tenantId,
    branch_id: resolved.branchId,
    branchId: resolved.branchId,
    branch_code: resolved.branchCode || current.branch_code || current.branchCode || '',
    branchCode: resolved.branchCode || current.branchCode || current.branch_code || '',
    branch_slug: resolved.branchSlug || current.branch_slug || current.branchSlug || '',
    branchSlug: resolved.branchSlug || current.branchSlug || current.branch_slug || '',
    branch_name: resolved.branchName || current.branch_name || current.branchName || '',
    branchName: resolved.branchName || current.branchName || current.branch_name || '',
    agent_id: resolved.agentId,
    agentId: resolved.agentId,
    connector_id: resolved.agentId,
    agent_key_id: resolved.agentKeyId,
    agentKeyId: resolved.agentKeyId,
    connector_key_id: resolved.agentKeyId,
    device_id: input.deviceId,
    deviceId: input.deviceId,
    hardware_id: input.hardwareId || input.deviceId,
    hardwareId: input.hardwareId || input.deviceId,
    hwid: input.hardwareId || input.deviceId,
    install_id: input.installId,
    installId: input.installId,
    provisioning_status: 'active',
    provisioningStatus: 'active',
    license_status: resolved.licenseStatus || current.license_status || 'active',
    heartbeat_interval_seconds: resolved.heartbeatIntervalSeconds || current.heartbeat_interval_seconds || 30,
    sync_interval_seconds: resolved.syncIntervalSeconds || current.sync_interval_seconds || 10,
    features,
    updated_at: now,
  };
}

function enrichLocalConfigWithLicense(config, license) {
  if (!license || license.skipped === true) return config;
  const identity = normalizeLicenseIdentity(license);
  const tenantId = identity.tenantId || config.tenant_id || config.tenantId || '';
  const branchId = identity.branchId || config.branch_id || config.branchId || '';
  const branchCode = identity.branchCode || config.branch_code || config.branchCode || '';
  const branchSlug = identity.branchSlug || config.branch_slug || config.branchSlug || '';
  const branchName = identity.branchName || config.branch_name || config.branchName || '';
  return {
    ...config,
    tenant_id: tenantId,
    tenantId,
    branch_id: branchId,
    branchId,
    branch_code: branchCode,
    branchCode,
    branch_slug: branchSlug,
    branchSlug,
    branch_name: branchName,
    branchName,
  };
}

async function postJson(url, body, headers = {}, fetchImpl = global.fetch) {
  if (typeof fetchImpl !== 'function') throw new Error('fetch no disponible para provisioning');
  const response = await fetchImpl(url, {
    method: 'POST',
    headers: { 'content-type': 'application/json', ...headers },
    body: JSON.stringify(body),
  });
  const text = await response.text();
  const payload = text ? JSON.parse(text) : {};
  if (!response.ok || payload.ok === false) {
    const mapped = buildProvisioningError(payload, response.status);
    const err = new Error(mapped.message);
    err.code = mapped.code;
    err.status = response.status;
    err.payload = payload;
    err.safeMessage = mapped.message;
    throw err;
  }
  return payload;
}

async function validateLicense({ cloudUrl, agentId, agentKeyId, agentSecret, tenantId, branchId, deviceId, hardwareId, version }, fetchImpl = global.fetch) {
  const body = JSON.stringify({
    tenantId,
    branchId,
    hwid: hardwareId || deviceId,
    deviceId,
    version,
    channel: 'windows-pilot',
    request: { module: 'updater', action: 'check_update' },
  });
  const endpoint = '/api/license/validate';
  const headers = authHeaders({
    agentId,
    agentKeyId,
    agentKey: agentSecret,
  }, { method: 'POST', path: endpoint, body });
  return postJson(`${cloudUrl}${endpoint}`, JSON.parse(body), headers, fetchImpl);
}

function saveEntitlementIfAllowed(license, configPath) {
  if (license && license.skipped !== true) {
    entitlementCache.saveEntitlement(license, {
      cachePath: path.join(configPath, 'entitlement-cache.enc'),
    });
  }
}

async function provisionResolved(options = {}, endpoint, bootstrapBody) {
  const configPath = options.configPath || process.env.CONFIG_PATH || path.join(process.cwd(), 'config');
  const connectorPath = path.join(configPath, 'connector.json');
  const secretPath = path.join(configPath, 'connector-secret.enc');
  const current = readJson(connectorPath, {});
  const cloudUrl = normalizeCloudUrl(options.cloudUrl || current.cloud_url || current.cloudUrl || process.env.ARKHON_CLOUD_BASE_URL || process.env.ARKHON_CLOUD_URL);
  const input = {
    deviceId: options.deviceId || current.device_id || current.deviceId || process.env.ARKHON_DEVICE_ID,
    installId: options.installId || current.install_id || current.installId || process.env.ARKHON_INSTALL_ID,
    hardwareId: options.hardwareId || current.hwid || current.hardware_id || process.env.ARKHON_HARDWARE_ID || getHardwareId(),
    hostname: options.hostname || os.hostname(),
    version: options.version || require('../../package.json').version,
  };
  if (!input.deviceId) throw new Error('deviceId requerido');
  if (!input.installId) throw new Error('installId requerido');

  const body = {
    ...bootstrapBody,
    deviceId: input.deviceId,
    installId: input.installId,
    hardwareId: input.hardwareId || input.deviceId,
    hostname: input.hostname,
    machineName: input.hostname,
    version: input.version,
    appVersion: input.version,
    capabilities: options.capabilities || ['updater', 'runtime-zip', 'billing_qr_printing'],
  };
  const bootstrap = await postJson(`${cloudUrl}${endpoint}`, body, {}, options.fetch);
  const resolved = normalizeBootstrapResponse({ ...bootstrap, cloudUrl: bootstrap.cloudUrl || cloudUrl });
  resolved.features = bootstrap.features || {};
  resolved.licenseStatus = bootstrap.license_status || bootstrap.licenseStatus || bootstrap.license?.status || 'active';
  resolved.heartbeatIntervalSeconds = bootstrap.heartbeat_interval_seconds || bootstrap.heartbeatIntervalSeconds || bootstrap.sync?.heartbeat_interval_seconds;
  resolved.syncIntervalSeconds = bootstrap.sync_interval_seconds || bootstrap.syncIntervalSeconds || bootstrap.sync?.sync_interval_seconds;
  const nextConfig = buildLocalConfig(current, resolved, input);
  writeJson(connectorPath, nextConfig);
  saveCredentials(secretPath, {
    ARKHON_CONNECTOR_SECRET: resolved.agentSecret,
    ARKHON_AGENT_KEY: resolved.agentSecret,
  });

  const license = options.skipLicenseValidation
    ? { skipped: true }
    : await validateLicense({
      cloudUrl: resolved.cloudUrl,
      agentId: resolved.agentId,
      agentKeyId: resolved.agentKeyId,
      agentSecret: resolved.agentSecret,
      tenantId: resolved.tenantId,
      branchId: resolved.branchId,
      deviceId: input.deviceId,
      hardwareId: input.hardwareId,
      version: input.version,
    }, options.fetch);
  saveEntitlementIfAllowed(license, configPath);
  const enrichedConfig = enrichLocalConfigWithLicense(nextConfig, license);
  if (JSON.stringify(enrichedConfig) !== JSON.stringify(nextConfig)) {
    writeJson(connectorPath, enrichedConfig);
  }

  return {
    ok: true,
    connectorPath,
    secretPath,
    provisioningStatus: 'active',
    canSign: Boolean(resolved.agentId && resolved.agentKeyId && resolved.agentSecret),
    tenantId: mask(resolved.tenantId),
    branchId: mask(resolved.branchId),
    agentId: mask(resolved.agentId),
    agentKeyId: mask(resolved.agentKeyId),
    license,
  };
}

async function provisionWithClaimCode(options = {}) {
  if (!options.claimCode) throw new Error('claimCode requerido');
  return provisionResolved(options, '/api/provisioning/bootstrap', {
    claimCode: options.claimCode,
  });
}

async function provisionWithControlNumber(options = {}) {
  const controlNumber = options.controlNumber || options.control_number;
  const controlPassword = options.controlPassword || options.control_password;
  if (!controlNumber) throw new Error('controlNumber requerido');
  if (!controlPassword) throw new Error('controlPassword requerido');
  return provisionResolved(options, '/api/provisioning/bootstrap-control', {
    controlNumber,
    control_password: controlPassword,
  });
}

module.exports = {
  mask,
  PROVISIONING_ERROR_MESSAGES,
  buildProvisioningError,
  normalizeBootstrapResponse,
  buildLocalConfig,
  enrichLocalConfigWithLicense,
  validateLicense,
  provisionWithClaimCode,
  provisionWithControlNumber,
};
