/**
 * MORPH-SPEC Cost Calculator Library
 *
 * Calculates Azure infrastructure costs from Bicep files.
 * Used both by CLI commands and internal automation.
 */

import { readFileSync, existsSync } from 'fs';
import { join, dirname } from 'path';
import { globSync } from 'glob';
import { fileURLToPath } from 'url';

const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);

// ============================================================================
// PRICING TABLE LOADER
// ============================================================================

/**
 * Load Azure pricing from JSON file
 * @param {string} pricingPath - Optional custom pricing file path
 * @returns {Object} Pricing table
 */
export function loadAzurePricing(pricingPath = null) {
  const defaultPath = join(__dirname, '..', '..', 'content', '.morph', 'config', 'azure-pricing.json');
  const pathToTry = pricingPath || defaultPath;

  if (!existsSync(pathToTry)) {
    // Fallback to hardcoded pricing (backwards compatibility)
    return AZURE_PRICING_FALLBACK;
  }

  try {
    const content = readFileSync(pathToTry, 'utf8');
    const pricingData = JSON.parse(content);
    return pricingData.resources || {};
  } catch (err) {
    console.warn(`Warning: Could not load pricing from ${pathToTry}: ${err.message}`);
    return AZURE_PRICING_FALLBACK;
  }
}

/**
 * Fallback pricing table (hardcoded for backwards compatibility)
 * @private
 */
const AZURE_PRICING_FALLBACK = {
  // Azure SQL Database
  'Microsoft.Sql/servers/databases': {
    'Free': 0,
    'Basic': 4.99,
    'S0': 14.72,
    'S1': 29.44,
    'S2': 73.61,
    'S3': 147.22,
    'S4': 294.44,
    'P1': 465.00,
    'P2': 930.00,
    'GP_Gen5_2': 328.00,
    'GP_Gen5_4': 656.00,
    'BC_Gen5_2': 1092.00,
  },

  // Azure Container Apps
  'Microsoft.App/containerApps': {
    'Consumption': 0,
    'Consumption_Active': 5.00,
    'Dedicated_D2': 55.00,
    'Dedicated_D4': 110.00,
  },

  // Azure Storage Account
  'Microsoft.Storage/storageAccounts': {
    'Standard_LRS': 0.02,
    'Standard_GRS': 0.05,
    'Premium_LRS': 0.15,
  },

  // Azure Application Insights
  'Microsoft.Insights/components': {
    'Free': 0,
    'Basic': 2.30,
  },

  // Azure Key Vault
  'Microsoft.KeyVault/vaults': {
    'Standard': 0.03,
    'Premium': 1.00,
  },

  // Azure Cosmos DB
  'Microsoft.DocumentDB/databaseAccounts': {
    'Serverless': 0,
    'Provisioned_400RU': 23.36,
    'Provisioned_1000RU': 58.40,
  },

  // Azure App Service
  'Microsoft.Web/serverfarms': {
    'F1': 0,
    'D1': 9.67,
    'B1': 13.14,
    'B2': 26.28,
    'S1': 69.35,
    'P1v2': 146.00,
  },

  // Azure Service Bus
  'Microsoft.ServiceBus/namespaces': {
    'Basic': 0.05,
    'Standard': 10.00,
    'Premium': 677.00,
  },

  // Azure Redis Cache
  'Microsoft.Cache/redis': {
    'C0': 16.06,
    'C1': 61.32,
    'C2': 122.63,
    'P1': 486.00,
  },

  // Azure Functions (Consumption Plan)
  'Microsoft.Web/sites': {
    'Consumption': 0,
    'Premium_EP1': 146.00,
  }
};

// ============================================================================
// PRICING TABLE - Azure (East US 2 / Brazil South)
// ============================================================================

/**
 * Azure pricing table (loaded from JSON)
 * @deprecated Use loadAzurePricing() for custom pricing paths
 */
export const AZURE_PRICING = loadAzurePricing();

// ============================================================================
// DEFAULT CONFIGURATION
// ============================================================================

export const DEFAULT_LIMITS = {
  freeTierOnly: 0,
  withApproval: 10,
  requiresADR: 10
};

// ============================================================================
// CONFIGURATION LOADER
// ============================================================================

/**
 * Load cost configuration from config.json
 * @param {string} configPath - Optional custom config path
 * @returns {Object} Cost configuration
 */
export function loadCostConfig(configPath = null) {
  const defaultPath = join(process.cwd(), '.morph/config/config.json');
  const templatePath = join(process.cwd(), 'content/.morph/config/config.template.json');

  const pathsToTry = [
    configPath,
    defaultPath,
    templatePath
  ].filter(Boolean);

  for (const path of pathsToTry) {
    if (existsSync(path)) {
      try {
        const content = readFileSync(path, 'utf8');
        const config = JSON.parse(content);

        if (config.costs && config.costs.limits) {
          return {
            limits: config.costs.limits,
            enforcement: config.costs.enforcement || {},
            currency: config.costs.currency || 'USD',
            region: config.costs.pricing?.region || 'eastus2'
          };
        }
      } catch (err) {
        // Continue to next path
      }
    }
  }

  // Fallback to defaults
  return {
    limits: DEFAULT_LIMITS,
    enforcement: {
      blockCommitsAboveLimit: true,
      requireADRInDecisionsMd: true,
      alertOnApproach: 0.8
    },
    currency: 'USD',
    region: 'eastus2'
  };
}

// ============================================================================
// BICEP PARSER
// ============================================================================

/**
 * Parse Bicep file and extract resources with SKUs
 * @param {string} filePath - Path to Bicep file
 * @returns {Array} Array of resource objects
 */
export function parseBicepFile(filePath) {
  if (!existsSync(filePath)) {
    throw new Error(`File not found: ${filePath}`);
  }

  const content = readFileSync(filePath, 'utf8');
  const resources = [];

  // Regex para extrair resources com conteúdo completo (inclui blocos aninhados)
  // Formato: resource <name> '<type>@<apiVersion>' = { ... }
  const resourceRegex = /resource\s+(\w+)\s+'([^']+)@[^']+'\s*=\s*\{/g;

  let match;
  while ((match = resourceRegex.exec(content)) !== null) {
    const [, name, type] = match;
    const startIndex = match.index + match[0].length;

    // Encontrar o fechamento do bloco { }
    let braceCount = 1;
    let endIndex = startIndex;

    while (braceCount > 0 && endIndex < content.length) {
      if (content[endIndex] === '{') braceCount++;
      if (content[endIndex] === '}') braceCount--;
      endIndex++;
    }

    const body = content.substring(startIndex, endIndex - 1);

    // Extrair SKU (dentro de bloco sku: { name: '...' })
    const skuBlockMatch = /sku:\s*\{([^}]+)\}/s.exec(body);
    let sku = null;

    if (skuBlockMatch) {
      const skuBlock = skuBlockMatch[1];
      const skuNameMatch = /name:\s*'([^']+)'/s.exec(skuBlock);
      sku = skuNameMatch ? skuNameMatch[1] : null;
    }

    // Extrair tier (alternativa, fora ou dentro do bloco sku)
    if (!sku) {
      const tierMatch = /tier:\s*'([^']+)'/s.exec(body);
      sku = tierMatch ? tierMatch[1] : null;
    }

    // Extrair minReplicas (Container Apps, deep nested)
    const minReplicasMatch = /minReplicas:\s*(\d+)/s.exec(body);
    const minReplicas = minReplicasMatch ? parseInt(minReplicasMatch[1], 10) : null;

    resources.push({
      name,
      type,
      sku,
      minReplicas,
      file: filePath
    });
  }

  return resources;
}

/**
 * Parse multiple Bicep files (supports glob patterns)
 * @param {string|Array} patterns - File path(s) or glob pattern(s)
 * @returns {Array} Array of all resources
 */
export function parseBicepFiles(patterns) {
  const patternArray = Array.isArray(patterns) ? patterns : [patterns];
  const allResources = [];

  for (const pattern of patternArray) {
    // Se for glob pattern, expandir
    const files = pattern.includes('*')
      ? globSync(pattern, { absolute: true })
      : [pattern];

    for (const file of files) {
      try {
        const resources = parseBicepFile(file);
        allResources.push(...resources);
      } catch (err) {
        // Silently skip files with errors (let caller handle)
        console.warn(`Warning: Could not parse ${file}: ${err.message}`);
      }
    }
  }

  return allResources;
}

// ============================================================================
// COST CALCULATOR
// ============================================================================

/**
 * Calculate cost of a single resource
 * @param {Object} resource - Resource object with type, sku, minReplicas
 * @returns {Object} Cost result with cost, warning, estimated, details
 */
export function calculateResourceCost(resource) {
  const pricing = AZURE_PRICING[resource.type];

  if (!pricing) {
    return {
      cost: 0,
      warning: `Unknown resource type: ${resource.type}`,
      estimated: false
    };
  }

  // Special case: Container Apps with minReplicas
  if (resource.type === 'Microsoft.App/containerApps') {
    const sku = resource.minReplicas > 0 ? 'Consumption_Active' : 'Consumption';
    const cost = pricing[sku] || 0;

    return {
      cost,
      warning: null,
      estimated: true,
      details: `minReplicas: ${resource.minReplicas}, scale-to-zero: ${resource.minReplicas === 0}`
    };
  }

  // General case: lookup by SKU
  if (resource.sku && pricing[resource.sku] !== undefined) {
    return {
      cost: pricing[resource.sku],
      warning: null,
      estimated: true
    };
  }

  // SKU not found
  return {
    cost: 0,
    warning: `Unknown SKU '${resource.sku}' for ${resource.type}`,
    estimated: false
  };
}

/**
 * Calculate total costs of all resources
 * @param {Array} resources - Array of resource objects
 * @param {Object} costConfig - Cost configuration
 * @returns {Object} Cost calculation result
 */
export function calculateTotalCosts(resources, costConfig) {
  let total = 0;
  const breakdown = [];
  const warnings = [];

  for (const resource of resources) {
    const result = calculateResourceCost(resource);

    total += result.cost;

    breakdown.push({
      name: resource.name,
      type: resource.type,
      sku: resource.sku || 'N/A',
      cost: result.cost,
      file: resource.file,
      estimated: result.estimated,
      details: result.details
    });

    if (result.warning) {
      warnings.push(result.warning);
    }

    // Warning para recursos caros
    if (result.cost > costConfig.limits.withApproval) {
      warnings.push(
        `Resource '${resource.name}' (${resource.type} ${resource.sku}) costs $${result.cost.toFixed(2)}/month - exceeds approval threshold!`
      );
    }
  }

  return {
    monthly: parseFloat(total.toFixed(2)),
    currency: costConfig.currency,
    region: costConfig.region,
    breakdown,
    warnings,
    limits: costConfig.limits,
    requiresApproval: total > costConfig.limits.freeTierOnly,
    requiresADR: total > costConfig.limits.requiresADR,
    isFreeTier: total === 0
  };
}

/**
 * Calculate costs from Bicep file(s) - Main entry point
 * @param {string|Array} bicepFiles - Bicep file path(s) or glob pattern(s)
 * @param {Object} options - Options
 * @param {string} options.configPath - Optional custom config path
 * @returns {Object} Complete cost calculation result
 */
export function calculateBicepCost(bicepFiles, options = {}) {
  const costConfig = loadCostConfig(options.configPath);
  const resources = parseBicepFiles(bicepFiles);

  if (resources.length === 0) {
    return {
      monthly: 0,
      currency: costConfig.currency,
      region: costConfig.region,
      breakdown: [],
      warnings: ['No resources found in Bicep files'],
      limits: costConfig.limits,
      requiresApproval: false,
      requiresADR: false,
      isFreeTier: true
    };
  }

  return calculateTotalCosts(resources, costConfig);
}
