function months(years) {
  return Math.round(Number(years) * 12);
}

function method(code, family, label, formula, options = {}) {
  return {
    code,
    family,
    label,
    formula,
    ...options
  };
}

function macrsDeclining(prefix, labelPrefix, system, multiplier, lives, options = {}) {
  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
    }
  ));
}

function straightLineMethods(prefix, family, labelPrefix, system, lives, options = {}) {
  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
    }
  ));
}

function acrsRateMethod(code, label, life, rates, options = {}) {
  return method(code, 'ACRS', label, 'rate_table', {
    system: 'ACRS',
    lifeMonths: months(life),
    defaultConvention: 'none',
    rates,
    ...options
  });
}

function buildMacrsMethods() {
  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.'
    })
  ];
}

function buildAcrsMethods() {
  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' })
  ];

  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'
  });

  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];
}

function buildGaapMethods() {
  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 })
  ];
}

function buildSpecialMethods() {
  return [
    method('manual', 'Manual', 'Manual depreciation entry', 'manual'),
    method('amortization', 'Lease', 'Straight-line amortization', 'straight_line')
  ];
}

function buildDepreciationMethods() {
  return [
    ...buildMacrsMethods(),
    ...buildAcrsMethods(),
    ...buildGaapMethods(),
    ...buildSpecialMethods()
  ];
}

function expandRuleSet(ruleSet) {
  const methods = buildDepreciationMethods();
  return {
    ...ruleSet,
    methodCatalog: ruleSet.methodCatalog || 'mixedassets-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
  };
}

module.exports = {
  buildDepreciationMethods,
  expandRuleSet
};
