const { all, audit, now, one, run } = require('../db');
const { getLimit } = require('./section179Limits');

// §179 return-level taxable-income limitation and carryover (IRS Pub 946 / IRC §179(b)). The per-asset
// engine already caps each asset's §179 at the dollar limit and phase-out; THIS layer aggregates the
// §179 elected across a company's assets for a tax year, applies the single annual dollar limit (reduced
// by the investment phase-out) AND the business taxable-income limitation, and carries any disallowed
// amount forward to the next year. Stateful: the prior year's carryover feeds the next year's available
// deduction. Per company + book + year.

function yearOf(value) {
  const s = String(value || '');
  return s ? Number(s.slice(0, 4)) : 0;
}
function round2(n) {
  return Math.round((Number(n) || 0) * 100) / 100;
}

// §179 elected per asset and the cost of §179 property placed in service, for a (company, book, year).
function electedFor(companyId, bookCode, year) {
  const rows = all(
    `SELECT a.acquisition_cost, a.in_service_date, a.acquired_date, b.section_179_amount, b.cost
     FROM assets a JOIN asset_books b ON b.asset_id = a.id
     WHERE a.entity_id = ? AND b.book_type = ? AND b.active = 1 AND a.status != 'disposed'`,
    [companyId, bookCode]
  );
  let elected = 0;
  let propertyCost = 0;
  let count = 0;
  for (const r of rows) {
    if (yearOf(r.in_service_date || r.acquired_date) !== Number(year)) continue;
    const amt = Number(r.section_179_amount || 0);
    if (amt <= 0) continue;
    elected += amt;
    propertyCost += Number(r.cost ?? r.acquisition_cost ?? 0);
    count += 1;
  }
  return { elected: round2(elected), propertyCost: round2(propertyCost), count };
}

function savedRow(companyId, bookCode, year) {
  return one('SELECT * FROM section_179_carryover WHERE entity_id = ? AND book_code = ? AND tax_year = ?', [companyId, bookCode, Number(year)]);
}

// Compute the limitation. `taxableIncome` overrides the stored value when provided (null/undefined uses
// the stored value, if any). The business-income limitation is only applied when an income figure exists.
function summary(companyId, bookCode, year, taxableIncome) {
  const yr = Number(year);
  const limitRow = getLimit(yr);
  const { elected, propertyCost, count } = electedFor(companyId, bookCode, yr);
  const priorRow = savedRow(companyId, bookCode, yr - 1);
  const priorCarryover = round2(priorRow ? priorRow.carryover_to_next : 0);

  const baseLimit = limitRow ? Number(limitRow.base_limit) : Infinity;
  const phaseoutThreshold = limitRow ? Number(limitRow.phaseout_threshold) : Infinity;
  const phaseoutReduction = Number.isFinite(phaseoutThreshold) ? Math.max(0, propertyCost - phaseoutThreshold) : 0;
  const dollarLimit = Number.isFinite(baseLimit) ? Math.max(0, baseLimit - phaseoutReduction) : Infinity;

  const available = round2(elected + priorCarryover);
  const dollarAllowed = round2(Math.min(available, dollarLimit));

  const stored = savedRow(companyId, bookCode, yr);
  const income = taxableIncome !== undefined && taxableIncome !== null
    ? Number(taxableIncome)
    : (stored && stored.taxable_income !== null ? Number(stored.taxable_income) : null);
  const incomeApplied = income !== null && !Number.isNaN(income);
  const allowed = round2(incomeApplied ? Math.min(dollarAllowed, Math.max(0, income)) : dollarAllowed);
  const carryover = round2(Math.max(0, available - allowed));

  return {
    company_id: companyId,
    book_code: bookCode,
    tax_year: yr,
    asset_count: count,
    elected,
    property_cost: propertyCost,
    prior_carryover: priorCarryover,
    base_limit: Number.isFinite(baseLimit) ? baseLimit : null,
    phaseout_threshold: Number.isFinite(phaseoutThreshold) ? phaseoutThreshold : null,
    phaseout_reduction: round2(phaseoutReduction),
    dollar_limit: Number.isFinite(dollarLimit) ? round2(dollarLimit) : null,
    available,
    dollar_allowed: dollarAllowed,
    taxable_income: incomeApplied ? round2(income) : null,
    income_limited: incomeApplied && allowed < dollarAllowed,
    allowed_deduction: allowed,
    carryover_to_next: carryover
  };
}

// Persist the entered taxable income and the resulting carryover (so the next year picks it up). Audited.
function save(companyId, bookCode, year, taxableIncome, userId) {
  const result = summary(companyId, bookCode, year, taxableIncome);
  const income = taxableIncome === undefined || taxableIncome === null || taxableIncome === '' ? null : Number(taxableIncome);
  run(
    `INSERT INTO section_179_carryover (entity_id, book_code, tax_year, taxable_income, carryover_to_next, updated_by, updated_at)
     VALUES (?, ?, ?, ?, ?, ?, ?)
     ON CONFLICT(entity_id, book_code, tax_year) DO UPDATE SET
       taxable_income = excluded.taxable_income, carryover_to_next = excluded.carryover_to_next,
       updated_by = excluded.updated_by, updated_at = excluded.updated_at`,
    [companyId, bookCode, Number(year), income, result.carryover_to_next, userId, now()]
  );
  audit(userId, 'update', 'section_179_limitation', `${bookCode}:${year}`, null, { taxable_income: income, carryover: result.carryover_to_next });
  return result;
}

module.exports = { summary, save };
