/*
  This module defines the catalog of depreciation methods available for use in rules. It includes functions to build the full catalog of methods, as well as helper functions to create specific types of methods (e.g., MACRS, ACRS, GAAP). The main function `expandRuleSet` takes a rule set and adds the full method catalog to it, along with counts of methods by family.
*/

const moment = require('moment');

// Helper function to convert years to months, with logging for debugging
function months(years) {
  console.log(`${moment().format()} 🕒 Converting years to months: ${years} years is ${Math.round(Number(years) * 12)} months`);
  return Math.round(Number(years) * 12);
}

// Factory function to create a depreciation method object, with logging for debugging
function method(code, family, label, formula, options = {}) {
  console.log(`${moment().format()} 🕒 Creating method: ${code}`);
  return {
    code,
    family,
    label,
    formula,
    ...options
  };
}

// Functions to build specific types of methods, with logging to trace the creation process
function macrsDeclining(prefix, labelPrefix, system, multiplier, lives, options = {}) {
  console.log(`${moment().format()} 🕒 Creating MACRS declining balance methods for lives: ${lives.join(', ')}`);
  return lives.map((life) => method(
    `${prefix}_${String(life).replace('.', '_')}`,
    'MACRS',
    `${labelPrefix} ${life}-year`,
    'macrs_declining_balance',
    {
      system,
      lifeMonths: months(life),
      rateMultiplier: multiplier,
      switchToStraightLine: true,
      defaultConvention: life >= 27.5 ? 'mid_month' : 'half_year',
      ...options
    }
  ));
}

// Straight-line methods for a range of lives, with logging to trace the creation process
function straightLineMethods(prefix, family, labelPrefix, system, lives, options = {}) {
  console.log(`${moment().format()} 🕒 Creating straight-line methods for lives: ${lives.join(', ')}`);
  return lives.map((life) => method(
    `${prefix}_${String(life).replace('.', '_')}`,
    family,
    `${labelPrefix} ${life}-year`,
    'straight_line',
    {
      system,
      lifeMonths: months(life),
      defaultConvention: life >= 27.5 ? 'mid_month' : 'half_year',
      ...options
    }
  ));
}

// ACRS methods based on specified rates, with logging to trace the creation process
function acrsRateMethod(code, label, life, rates, options = {}) {
  console.log(`${moment().format()} 🕒 Creating ACRS rate method: ${code}`);
  return method(code, 'ACRS', label, 'rate_table', {
    system: 'ACRS',
    lifeMonths: months(life),
    defaultConvention: 'none',
    rates,
    ...options
  });
}

// Main function to build the full catalog of depreciation methods, with logging to trace the overall process
function buildMacrsMethods() {
  console.log(`${moment().format()} 🕒 Building MACRS methods`);
  return [
    ...macrsDeclining('macrs_gds_200db', 'MACRS GDS 200% DB', 'GDS', 2, [3, 5, 7, 10]),
    ...macrsDeclining('macrs_gds_150db', 'MACRS GDS 150% DB', 'GDS', 1.5, [3, 5, 7, 10, 15, 20]),
    ...straightLineMethods('macrs_gds_sl', 'MACRS', 'MACRS GDS straight-line', 'GDS', [3, 5, 7, 10, 15, 20, 27.5, 39]),
    ...straightLineMethods('macrs_ads_sl', 'MACRS', 'MACRS ADS straight-line', 'ADS', [3, 5, 7, 10, 12, 15, 20, 30, 40]),
    ...macrsDeclining('macrs_ads_150db_legacy', 'Legacy MACRS ADS 150% DB', 'ADS', 1.5, [3, 5, 7, 10, 12, 15, 20, 30, 40], {
      legacy: true,
      sourceNote: 'For property where an older ADS 150% DB election must continue.'
    })
  ];
}

// ACRS methods, including regular and alternate straight-line options, with logging to trace the creation process
function buildAcrsMethods() {
  console.log(`${moment().format()} 🕒 Building ACRS methods`);
  const regular = [
    acrsRateMethod('acrs_regular_3', 'ACRS regular 3-year', 3, [0.25, 0.38, 0.37]),
    acrsRateMethod('acrs_regular_5', 'ACRS regular 5-year', 5, [0.15, 0.22, 0.21, 0.21, 0.21]),
    acrsRateMethod('acrs_regular_10', 'ACRS regular 10-year', 10, [0.08, 0.14, 0.12, 0.10, 0.10, 0.10, 0.09, 0.09, 0.09, 0.09]),
    acrsRateMethod('acrs_regular_15_real', 'ACRS regular 15-year real property', 15, [], { requiresMonthTable: true, fallbackFormula: 'straight_line' }),
    acrsRateMethod('acrs_regular_15_low_income', 'ACRS regular 15-year low-income housing', 15, [], { requiresMonthTable: true, fallbackFormula: 'straight_line' }),
    acrsRateMethod('acrs_regular_18_real', 'ACRS regular 18-year real property', 18, [], { requiresMonthTable: true, fallbackFormula: 'straight_line' }),
    acrsRateMethod('acrs_regular_19_real', 'ACRS regular 19-year real property', 19, [], { requiresMonthTable: true, fallbackFormula: 'straight_line' }),
    acrsRateMethod('acrs_regular_listed_property', 'ACRS listed property predominant-use table', 5, [], { requiresListedPropertyTable: true, fallbackFormula: 'straight_line' })
  ];

  // ACRS alternate straight-line methods for the same lives, with logging to trace the creation process
  const alternate = straightLineMethods('acrs_alternate_sl', 'ACRS', 'ACRS alternate modified straight-line', 'ACRS', [3, 5, 10, 12, 15, 18, 19, 25], {
    formula: 'acrs_alternate_straight_line',
    defaultConvention: 'half_year'
  });

  // ACRS required straight-line methods for certain lives, with logging to trace the creation process
  const straightLine = straightLineMethods('acrs_required_sl', 'ACRS', 'ACRS required straight-line', 'ACRS', [3, 5, 10, 15, 18, 19, 35, 45], {
    formula: 'straight_line',
    defaultConvention: 'half_year',
    legacy: true
  });

  return [...regular, ...alternate, ...straightLine];
}

// GAAP methods, including straight-line, sum-of-years-digits, and various declining balance options, with logging to trace the creation process
function buildGaapMethods() {
  console.log(`${moment().format()} 🕒 Building GAAP methods`);
  return [
    method('straight_line', 'GAAP', 'Straight-line', 'straight_line'),
    method('sum_of_years_digits', 'GAAP', 'Sum-of-years-digits', 'sum_of_years_digits'),
    method('declining_balance_200', 'GAAP', '200% declining balance', 'declining_balance', { rateMultiplier: 2 }),
    method('declining_balance_175', 'GAAP', '175% declining balance', 'declining_balance', { rateMultiplier: 1.75 }),
    method('declining_balance_150', 'GAAP', '150% declining balance', 'declining_balance', { rateMultiplier: 1.5 }),
    method('declining_balance_125', 'GAAP', '125% declining balance', 'declining_balance', { rateMultiplier: 1.25 })
  ];
}

// Special methods that don't fit into the main families, with logging to trace the creation process
function buildSpecialMethods() {
  console.log(`${moment().format()} 🕒 Building special methods`);
  return [
    method('manual', 'Manual', 'Manual depreciation entry', 'manual'),
    method('amortization', 'Lease', 'Straight-line amortization', 'straight_line')
  ];
}

// Sage-style 200% declining-balance method codes. The MACRS family (MF/MT/MI) ignores
// salvage value and recovers the full basis; the standard family (DB/DH/DD) honors salvage
// (depreciates only down to it). All use a 200% rate and switch to straight-line.
function build200Methods() {
  console.log(`${moment().format()} 🕒 Building 200% declining balance methods`);
  return [
    method('MF200', 'MACRS', 'MF200 — MACRS Formula, 200% DB (GDS)', 'macrs_declining_balance', {
      system: 'GDS', rateMultiplier: 2, switchToStraightLine: true, defaultConvention: 'half_year', ignoreSalvage: true
    }),
    method('MT200', 'MACRS', 'MT200 — MACRS Tables, 200% DB (GDS)', 'macrs_declining_balance', {
      system: 'GDS', rateMultiplier: 2, switchToStraightLine: true, defaultConvention: 'half_year', ignoreSalvage: true, usesTables: true
    }),
    method('MI200', 'MACRS', 'MI200 — MACRS 200% DB, Indian Reservation', 'macrs_declining_balance', {
      system: 'GDS', rateMultiplier: 2, switchToStraightLine: true, defaultConvention: 'half_year', ignoreSalvage: true, indianReservation: true
    }),
    method('DB200', 'Declining Balance', 'DB200 — 200% Declining Balance to SL (uses book convention)', 'declining_balance', {
      rateMultiplier: 2, switchToStraightLine: true
    }),
    method('DH200', 'Declining Balance', 'DH200 — 200% Declining Balance to SL, Half-Year', 'declining_balance', {
      rateMultiplier: 2, switchToStraightLine: true, defaultConvention: 'half_year'
    }),
    method('DD200', 'Declining Balance', 'DD200 — 200% Declining Balance to SL, Full-Year', 'declining_balance', {
      rateMultiplier: 2, switchToStraightLine: true, defaultConvention: 'none'
    })
  ];
  console.log(`${moment().format()} 🕒 Completed building 200% declining balance methods`);
}

// Main function to build the full catalog of depreciation methods, with logging to trace the overall process
function buildDepreciationMethods() {
  console.log(`${moment().format()} 🕒 Building full depreciation method catalog`);
  return [
    ...build200Methods(),
    ...buildMacrsMethods(),
    ...buildAcrsMethods(),
    ...buildGaapMethods(),
    ...buildSpecialMethods()
  ];
  console.log(`${moment().format()} 🕒 Completed building full depreciation method catalog with ${methods.length} methods`);
}

// Function to expand a rule set with the full method catalog and counts of methods by family, with logging to trace the process
function expandRuleSet(ruleSet) {
  console.log(`${moment().format()} 🕒 Expanding rule set with method catalog`);
  const methods = buildDepreciationMethods();
  return {
    ...ruleSet,
    methodCatalog: ruleSet.methodCatalog || 'deprecore-68-us-tax-and-accounting-v1',
    methodCounts: {
      macrs: methods.filter((item) => item.family === 'MACRS').length,
      acrs: methods.filter((item) => item.family === 'ACRS').length,
      gaap: methods.filter((item) => item.family === 'GAAP').length,
      supplemental: methods.filter((item) => !['MACRS', 'ACRS', 'GAAP'].includes(item.family)).length,
      total: methods.length
    },
    methods
  };
}

// Export the main functions for building the method catalog and expanding rule sets, with logging to indicate when these functions are called
console.log(`${moment().format()} 🕒 Initializing rule catalog module`);
module.exports = {
  buildDepreciationMethods,
  expandRuleSet
};
