/* CA Automator — Business Advisory engines (JS mirror of
   server/business_engines.py). Kept numerically identical to the Python
   engine — every change here must be made there too, and vice versa. */
(function (global) {
  'use strict';

  const R2 = x => Math.round((Number(x) || 0) * 100) / 100;
  const N = (v, dflt) => (v === null || v === undefined || v === '') ? (dflt || 0) : (isFinite(Number(v)) ? Number(v) : (dflt || 0));
  const safeDiv = (a, b) => b ? (a / b) : 0;

  function profitabilityAnalysis(lines) {
    const groups = {};
    (lines || []).forEach(r => {
      const dt = r.dimension_type || 'overall', dn = r.dimension_name || '';
      const key = dt + '' + dn;
      const g = groups[key] || (groups[key] = { dimension_type: dt, dimension_name: dn, revenue: 0, direct_cost: 0, allocated_overhead: 0 });
      g.revenue += N(r.revenue); g.direct_cost += N(r.direct_cost); g.allocated_overhead += N(r.allocated_overhead);
    });
    const groupList = Object.keys(groups).map(k => groups[k]);
    const totalRevenue = groupList.reduce((s, g) => s + g.revenue, 0);
    let rows = groupList.map(g => {
      const grossMargin = g.revenue - g.direct_cost;
      const netMargin = grossMargin - g.allocated_overhead;
      return { dimension_type: g.dimension_type, dimension_name: g.dimension_name,
        revenue: R2(g.revenue), direct_cost: R2(g.direct_cost), allocated_overhead: R2(g.allocated_overhead),
        gross_margin: R2(grossMargin), gross_margin_pct: R2(safeDiv(grossMargin, g.revenue) * 100),
        net_margin: R2(netMargin), net_margin_pct: R2(safeDiv(netMargin, g.revenue) * 100),
        revenue_share_pct: R2(safeDiv(g.revenue, totalRevenue) * 100) };
    });
    rows = rows.sort((a, b) => b.net_margin - a.net_margin);

    const totalDirectCost = groupList.reduce((s, g) => s + g.direct_cost, 0);
    const totalOverhead = groupList.reduce((s, g) => s + g.allocated_overhead, 0);
    const totalGrossMargin = totalRevenue - totalDirectCost;
    const totalNetMargin = totalGrossMargin - totalOverhead;
    const costStructure = {
      total_revenue: R2(totalRevenue), total_direct_cost: R2(totalDirectCost), total_overhead: R2(totalOverhead),
      direct_cost_pct_of_revenue: R2(safeDiv(totalDirectCost, totalRevenue) * 100),
      overhead_pct_of_revenue: R2(safeDiv(totalOverhead, totalRevenue) * 100),
      total_gross_margin: R2(totalGrossMargin), total_gross_margin_pct: R2(safeDiv(totalGrossMargin, totalRevenue) * 100),
      total_net_margin: R2(totalNetMargin), total_net_margin_pct: R2(safeDiv(totalNetMargin, totalRevenue) * 100),
    };
    const lossMakers = rows.filter(r => r.net_margin < 0);
    return { rows, cost_structure: costStructure, loss_makers: lossMakers,
      best_performer: rows.length ? rows[0] : null, worst_performer: rows.length ? rows[rows.length - 1] : null };
  }

  function pricingRecommendation(inputs) {
    inputs = inputs || {};
    const unitCost = N(inputs.unit_cost);
    const targetMarginPct = N(inputs.target_margin_pct);
    const denom = 1 - (targetMarginPct / 100);
    const recommendedPrice = denom > 0 ? (unitCost / denom) : null;
    const markupPct = recommendedPrice !== null ? R2(safeDiv(recommendedPrice - unitCost, unitCost) * 100) : null;

    const out = { unit_cost: R2(unitCost), target_margin_pct: targetMarginPct,
      recommended_price: recommendedPrice !== null ? R2(recommendedPrice) : null, markup_pct: markupPct };
    if (inputs.competitor_price !== null && inputs.competitor_price !== undefined && inputs.competitor_price !== '') {
      const cp = N(inputs.competitor_price);
      out.competitor_price = R2(cp);
      out.gap_vs_competitor = R2((recommendedPrice || 0) - cp);
      out.gap_vs_competitor_pct = cp ? R2(safeDiv((recommendedPrice || 0) - cp, cp) * 100) : null;
    }
    if (inputs.current_price !== null && inputs.current_price !== undefined && inputs.current_price !== '') {
      const cur = N(inputs.current_price);
      out.current_price = R2(cur);
      out.current_margin_pct = cur ? R2(safeDiv(cur - unitCost, cur) * 100) : null;
      out.price_change_needed = R2((recommendedPrice || 0) - cur);
    }
    return out;
  }

  function budgetVariance(lines) {
    const rows = (lines || []).map(r => {
      const lineType = r.line_type || 'expense';
      const budgeted = N(r.budgeted_amount);
      const hasActual = r.actual_amount !== null && r.actual_amount !== undefined && r.actual_amount !== '';
      const actualV = N(r.actual_amount);
      const variance = hasActual ? (actualV - budgeted) : null;
      const variancePct = (hasActual && budgeted) ? R2(safeDiv(variance, budgeted) * 100) : null;
      let favorable = null;
      if (hasActual) favorable = lineType === 'revenue' ? (variance >= 0) : (variance <= 0);
      return { id: r.id, category: r.category || '', line_type: lineType, period_label: r.period_label || '',
        budgeted_amount: R2(budgeted), actual_amount: hasActual ? R2(actualV) : null,
        variance: hasActual ? R2(variance) : null, variance_pct: variancePct, favorable };
    });

    function total(lineType, key) {
      return rows.filter(r => r.line_type === lineType && r[key] !== null).reduce((s, r) => s + N(r[key]), 0);
    }
    const budgetedRevenue = total('revenue', 'budgeted_amount'), actualRevenue = total('revenue', 'actual_amount');
    const budgetedExpense = total('expense', 'budgeted_amount'), actualExpense = total('expense', 'actual_amount');
    const summary = { budgeted_revenue: R2(budgetedRevenue), actual_revenue: R2(actualRevenue),
      budgeted_expense: R2(budgetedExpense), actual_expense: R2(actualExpense),
      budgeted_profit: R2(budgetedRevenue - budgetedExpense), actual_profit: R2(actualRevenue - actualExpense) };
    const unfavorable = rows.filter(r => r.favorable === false);
    return { rows, summary, unfavorable_count: unfavorable.length, unfavorable_lines: unfavorable };
  }

  function kpiDashboard(kpis, valuesByKpi) {
    const out = (kpis || []).map(k => {
      const vals = (valuesByKpi[k.id] || []).slice().sort((a, b) => (a.period_label || '').localeCompare(b.period_label || ''));
      const latest = vals.length ? vals[vals.length - 1] : null;
      const previous = vals.length >= 2 ? vals[vals.length - 2] : null;
      const latestValue = latest ? latest.actual_value : null;
      const previousValue = previous ? previous.actual_value : null;
      let trend = null;
      if (latestValue !== null && previousValue !== null) trend = latestValue > previousValue ? 'up' : (latestValue < previousValue ? 'down' : 'flat');
      const target = k.target_value;
      const higherIsBetter = k.higher_is_better === undefined ? true : !!k.higher_is_better;
      let onTarget = null, varianceVsTarget = null;
      if (latestValue !== null && target !== null && target !== undefined && target !== '') {
        varianceVsTarget = R2(latestValue - N(target));
        onTarget = higherIsBetter ? (latestValue >= N(target)) : (latestValue <= N(target));
      }
      return { id: k.id, name: k.name || '', category: k.category || '', unit: k.unit || '',
        target_value: target, higher_is_better: higherIsBetter,
        latest_period: latest ? latest.period_label : null, latest_value: latestValue !== null ? R2(latestValue) : null,
        previous_value: previousValue !== null ? R2(previousValue) : null, trend, variance_vs_target: varianceVsTarget,
        on_target: onTarget, history: vals.map(v => ({ period_label: v.period_label, actual_value: R2(v.actual_value) })) };
    });
    const offTarget = out.filter(k => k.on_target === false);
    return { kpis: out, off_target_count: offTarget.length };
  }

  global.BusinessEngines = { R2, profitabilityAnalysis, pricingRecommendation, budgetVariance, kpiDashboard };
})(window);
