/* global React, ReactDOM */
const { useState, useMemo } = React;

// === Math ===
const calcPanamaTax = (income) => {
  if (income <= 11000) return 0;
  if (income <= 50000) return (income - 11000) * 0.15;
  return (50000 - 11000) * 0.15 + (income - 50000) * 0.25;
};
const calcNPV = (cashflows, rate) =>
  cashflows.reduce((acc, cf, t) => acc + cf / Math.pow(1 + rate, t), 0);
const calcIRR = (cashflows) => {
  const hasNeg = cashflows.some(cf => cf < 0);
  const hasPos = cashflows.some(cf => cf > 0);
  if (!hasNeg || !hasPos) return null;
  let rate = 0.1;
  for (let i = 0; i < 100; i++) {
    let npv = 0, dnpv = 0;
    for (let t = 0; t < cashflows.length; t++) {
      const denom = Math.pow(1 + rate, t);
      npv += cashflows[t] / denom;
      if (t > 0) dnpv -= (t * cashflows[t]) / Math.pow(1 + rate, t + 1);
    }
    if (Math.abs(npv) < 1e-6) return rate;
    if (Math.abs(dnpv) < 1e-12) break;
    const newRate = rate - npv / dnpv;
    if (!isFinite(newRate)) break;
    if (Math.abs(newRate - rate) < 1e-8) return newRate;
    rate = newRate;
    if (rate < -0.99) rate = -0.99;
  }
  let low = -0.99, high = 10;
  let npvLow = calcNPV(cashflows, low);
  let npvHigh = calcNPV(cashflows, high);
  if (npvLow * npvHigh > 0) return null;
  for (let i = 0; i < 200; i++) {
    const mid = (low + high) / 2;
    const npvMid = calcNPV(cashflows, mid);
    if (Math.abs(npvMid) < 1e-6) return mid;
    if (npvMid * npvLow < 0) { high = mid; npvHigh = npvMid; }
    else { low = mid; npvLow = npvMid; }
  }
  return (low + high) / 2;
};

const formatUSD = (n) => {
  if (n === null || n === undefined || !isFinite(n)) return '—';
  return new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD', maximumFractionDigits: 0 }).format(n);
};
const formatPct = (n) => {
  if (n === null || n === undefined || !isFinite(n)) return '—';
  return `${(n * 100).toFixed(2)}%`;
};

// === Icons ===
const InfoIcon = () => (
  <svg className="cmp-tt-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><circle cx="12" cy="12" r="10"/><path d="M12 16v-4"/><path d="M12 8h.01"/></svg>
);
const AlertIcon = () => (
  <svg className="cmp-banner-icon" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/></svg>
);
const Chev = ({ up }) => (
  <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round">
    {up ? <polyline points="18 15 12 9 6 15"/> : <polyline points="6 9 12 15 18 9"/>}
  </svg>
);
const BookIcon = () => (
  <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20"/><path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z"/></svg>
);

// === Inputs ===
const Tooltip = ({ text }) => (
  <span className="cmp-tt">
    <InfoIcon />
    <span className="cmp-tt-body">{text}</span>
  </span>
);
const NumberInput = ({ value, onChange, prefix, suffix, step, min }) => (
  <div className="cmp-input-wrap">
    {prefix && <span className="cmp-input-prefix">{prefix}</span>}
    <input
      type="number"
      className={`cmp-input ${prefix ? 'has-prefix' : ''} ${suffix ? 'has-suffix' : ''}`}
      value={value}
      onChange={(e) => onChange(e.target.value === '' ? '' : Number(e.target.value))}
      step={step || 'any'}
      min={min}
    />
    {suffix && <span className="cmp-input-suffix">{suffix}</span>}
  </div>
);
const TextInput = ({ value, onChange, placeholder }) => (
  <input type="text" className="cmp-input" value={value} onChange={(e) => onChange(e.target.value)} placeholder={placeholder} />
);
const Toggle = ({ checked, onChange, label }) => (
  <button type="button" className={`cmp-toggle ${checked ? 'is-on' : ''}`} onClick={() => onChange(!checked)}>
    <span className="cmp-toggle-track"><span className="cmp-toggle-thumb"/></span>
    <span className="cmp-toggle-label">{label}</span>
  </button>
);

const defaultPolicy = (i) => ({
  name: `Policy ${i + 1}`,
  premium: 1200,
  withdrawal: 30000,
});

function App() {
  // Global required
  const [discountRate, setDiscountRate] = useState(3);
  const [numPolicies, setNumPolicies] = useState(2);
  const [years, setYears] = useState(20);

  // Global optional
  const [showOptional, setShowOptional] = useState(false);
  const [taxEnabled, setTaxEnabled] = useState(false);
  const [income, setIncome] = useState(30000);
  const [filingCost, setFilingCost] = useState(125);
  const [replacementEnabled, setReplacementEnabled] = useState(false);
  const [replacement, setReplacement] = useState(0);
  const [tranquilidadEnabled, setTranquilidadEnabled] = useState(false);
  const [tranquilidad, setTranquilidad] = useState(0);

  // Per-policy
  const [policies, setPolicies] = useState([defaultPolicy(0), defaultPolicy(1), defaultPolicy(2), defaultPolicy(3)]);
  const [showInfo, setShowInfo] = useState(false);

  const updatePolicy = (idx, field, value) => {
    setPolicies(prev => prev.map((p, i) => i === idx ? { ...p, [field]: value } : p));
  };

  const activePolicies = policies.slice(0, numPolicies);

  const results = useMemo(() => {
    const rate = (Number(discountRate) || 0) / 100;
    const yrs = Math.max(0, Math.floor(Number(years) || 0));
    return activePolicies.map((p, idx) => {
      const premium = Number(p.premium) || 0;
      const withdrawal = Number(p.withdrawal) || 0;
      let taxSaving = 0;
      if (taxEnabled) {
        const inc = Number(income) || 0;
        const fc = Number(filingCost) || 0;
        const taxWithout = calcPanamaTax(inc);
        const taxWith = calcPanamaTax(Math.max(0, inc - premium));
        taxSaving = (taxWithout - taxWith) - fc;
      }
      const rep = replacementEnabled ? (Number(replacement) || 0) : 0;
      const tranq = tranquilidadEnabled ? (Number(tranquilidad) || 0) : 0;
      const annualInflow = taxSaving + rep + tranq;
      const netAnnual = -premium + annualInflow;
      const cashflows = [];
      for (let t = 0; t < yrs; t++) cashflows.push(netAnnual);
      cashflows.push(withdrawal);
      const npv = calcNPV(cashflows, rate);
      const npc = -npv;
      const irr = calcIRR(cashflows);
      const totalCost = premium * yrs;
      const totalInflows = annualInflow * yrs;
      return { idx, name: p.name || `Policy ${idx + 1}`, premium, years: yrs, withdrawal, npc, irr, totalCost, totalInflows, annualInflow, taxSaving };
    });
  }, [activePolicies, discountRate, years, taxEnabled, income, filingCost, replacementEnabled, replacement, tranquilidadEnabled, tranquilidad]);

  const ranked = useMemo(() => [...results].sort((a, b) => a.npc - b.npc), [results]);
  const verdictMap = useMemo(() => {
    const map = {};
    ranked.forEach((r, rank) => {
      let label, cls;
      if (rank === 0) { label = 'Most efficient'; cls = 'cmp-badge-best'; }
      else if (rank === ranked.length - 1 && ranked.length > 2) { label = 'Least efficient'; cls = 'cmp-badge-worst'; }
      else { label = 'Alternative'; cls = 'cmp-badge-alt'; }
      map[r.idx] = { label, cls, rank: rank + 1 };
    });
    return map;
  }, [ranked]);

  const minNPC = ranked.length ? ranked[0].npc : 0;
  const maxNPC = ranked.length ? ranked[ranked.length - 1].npc : 0;
  const colsClass = `cols-${numPolicies}`;

  return (
    <div className="cmp-wrap">
      {/* Banners */}
      <div className="cmp-banner cmp-banner-blue">
        <AlertIcon />
        <div><strong>Comparison requirement:</strong> This tool assumes all policies have the <strong>same insured amount</strong>. The comparison is strictly on cost efficiency. If insured amounts differ, the results will artificially favor the cheaper policy without reflecting the difference in coverage.</div>
      </div>
      <div className="cmp-banner cmp-banner-gold">
        <AlertIcon />
        <div><strong>Important:</strong> A life insurance policy is not primarily an investment instrument — it's protection. It's normal for the return component, in isolation, not to exceed the opportunity cost of capital. Use this tool to compare financial efficiency between options, not to decide whether to carry life insurance.</div>
      </div>

      <button className="cmp-info-toggle" onClick={() => setShowInfo(!showInfo)}>
        <BookIcon /> {showInfo ? 'Hide' : 'What do NPC and IRR mean?'} <Chev up={showInfo} />
      </button>
      {showInfo && (
        <div className="cmp-info-card">
          <h3>Net Present Cost (NPC)</h3>
          <p>The total cost of the policy expressed in today's dollars, after discounting all future cash flows (premiums, withdrawal received and annual benefits) at the chosen discount rate.</p>
          <p><strong>Interpretation:</strong> "How much does this policy really cost me, in present value, after considering everything I get back?" <strong>Lower NPC = more efficient.</strong></p>
          <div className="cmp-info-note"><strong>Technical note:</strong> NPC is mathematically the opposite of NPV. A positive NPC means the present cost of premiums exceeds the present benefit of the withdrawal — expected in life insurance policies, since their main value lies in coverage.</div>
          <div className="cmp-info-divider"></div>
          <h3>Internal Rate of Return (IRR)</h3>
          <p>The effective annualized return the policy generates viewed as a pure investment. It's the discount rate that would make NPC equal zero.</p>
          <ul>
            <li><strong>IRR &gt; discount rate:</strong> The investment component beats the opportunity cost.</li>
            <li><strong>IRR &lt; discount rate:</strong> The client would earn more in their alternative investment.</li>
            <li><strong>IRR not computable:</strong> The cash flows don't allow a calculation.</li>
          </ul>
        </div>
      )}

      {/* Global parameters */}
      <div className="cmp-card">
        <div className="cmp-card-pad">
          <h2 className="cmp-section-title">Global parameters</h2>
          <div className="cmp-global-grid">
            <div>
              <label className="cmp-label">Discount rate <Tooltip text="Opportunity cost of money. What you'd earn if you invested elsewhere instead. 3% is conservative. Active investor: 5-7%." /></label>
              <NumberInput value={discountRate} onChange={setDiscountRate} suffix="%" step="0.1" min="0" />
            </div>
            <div>
              <label className="cmp-label">Number of policies to compare</label>
              <select className="cmp-input" value={numPolicies} onChange={(e) => setNumPolicies(Number(e.target.value))}>
                <option value={2}>2 policies</option>
                <option value={3}>3 policies</option>
                <option value={4}>4 policies</option>
              </select>
            </div>
            <div>
              <label className="cmp-label">Years to withdrawal <Tooltip text="Number of years you'll pay the premium before withdrawing the accumulated value. Must be the same across all policies for a fair comparison." /></label>
              <NumberInput value={years} onChange={setYears} suffix="yrs" min="1" step="1" />
            </div>
          </div>

          {/* Optional adjustments */}
          <div className="cmp-optional">
            <button className="cmp-optional-toggle" onClick={() => setShowOptional(!showOptional)}>
              <span>Optional adjustments</span>
              <Chev up={showOptional} />
            </button>
            {showOptional && (
              <div className="cmp-optional-body">
                <div className="cmp-opt-block">
                  <Toggle checked={taxEnabled} onChange={setTaxEnabled} label="Tax benefit" />
                  <p className="cmp-opt-desc">Life insurance premiums are deductible from income tax in Panama, reducing the real cost of the policy.</p>
                  {taxEnabled && (
                    <div className="cmp-opt-fields">
                      <div>
                        <label className="cmp-label-sm">Client's annual income <Tooltip text="Gross annual income before deductions. Used to calculate how much you save in taxes by deducting the premium." /></label>
                        <NumberInput value={income} onChange={setIncome} prefix="$" min="0" />
                      </div>
                      <div>
                        <label className="cmp-label-sm">Filing cost <Tooltip text="What your accountant charges to prepare and file the tax return where you deduct the premium. If you already file, enter $0." /></label>
                        <NumberInput value={filingCost} onChange={setFilingCost} prefix="$" min="0" />
                      </div>
                    </div>
                  )}
                </div>
                <div className="cmp-opt-block">
                  <Toggle checked={replacementEnabled} onChange={setReplacementEnabled} label="Replacement of other policies" />
                  <p className="cmp-opt-desc">If the new policy replaces existing coverage you already pay for, that annual savings is added as a benefit in the analysis.</p>
                  {replacementEnabled && (
                    <div className="cmp-opt-fields">
                      <div>
                        <label className="cmp-label-sm">Annual savings <Tooltip text="Total annual amount you'll stop paying for the policies that this new coverage replaces." /></label>
                        <NumberInput value={replacement} onChange={setReplacement} prefix="$" min="0" />
                      </div>
                    </div>
                  )}
                </div>
                <div className="cmp-opt-block">
                  <Toggle checked={tranquilidadEnabled} onChange={setTranquilidadEnabled} label="Peace-of-mind value" />
                  <p className="cmp-opt-desc">Subjective value you assign to the coverage. Useful for breaking ties between policies with similar NPC.</p>
                  {tranquilidadEnabled && (
                    <div className="cmp-opt-fields">
                      <div>
                        <label className="cmp-label-sm">Annual value <Tooltip text="How much would you pay per year for the peace of mind of knowing your beneficiaries would receive that sum if something happened to you? There's no right answer, it's personal." /></label>
                        <NumberInput value={tranquilidad} onChange={setTranquilidad} prefix="$" min="0" />
                      </div>
                    </div>
                  )}
                </div>
              </div>
            )}
          </div>
        </div>
      </div>

      {/* Policies */}
      <div className={`cmp-policy-grid ${colsClass}`}>
        {activePolicies.map((p, idx) => (
          <div key={idx} className="cmp-policy">
            <div className="cmp-policy-head">Policy {idx + 1}</div>
            <div className="cmp-policy-body">
              <div className="cmp-field">
                <label className="cmp-label-sm">Insurer / Plan</label>
                <TextInput value={p.name} onChange={(v) => updatePolicy(idx, 'name', v)} placeholder="e.g. ASSA Optimista" />
              </div>
              <div className="cmp-field">
                <label className="cmp-label-sm">Annual premium <Tooltip text="Amount you pay each year for this policy. You'll find it on the quote provided by the insurer." /></label>
                <NumberInput value={p.premium} onChange={(v) => updatePolicy(idx, 'premium', v)} prefix="$" min="0" />
              </div>
              <div className="cmp-field">
                <label className="cmp-label-sm">Withdrawal amount <Tooltip text="Accumulated value you'll receive at the time of withdrawal. Found in the policy's value table, at the year you selected above." /></label>
                <NumberInput value={p.withdrawal} onChange={(v) => updatePolicy(idx, 'withdrawal', v)} prefix="$" min="0" />
              </div>
            </div>
          </div>
        ))}
      </div>

      {/* Results */}
      <h2 className="cmp-results-title">Results</h2>

      <div className={`cmp-score-grid ${colsClass}`}>
        {results.map((r) => {
          const v = verdictMap[r.idx];
          const isLowest = r.npc === minNPC;
          const isHighest = r.npc === maxNPC && minNPC !== maxNPC;
          const irrBeatsRate = r.irr !== null && r.irr > (Number(discountRate) / 100);
          return (
            <div key={r.idx} className="cmp-score">
              <div className="cmp-score-top">
                <div className="cmp-score-name">{r.name}</div>
                <span className={`cmp-badge ${v.cls}`}>{v.label}</span>
              </div>
              <div className="cmp-score-body">
                <div className="cmp-score-row">
                  <div style={{ flex: 1 }}>
                    <div className="cmp-score-label">Net Present Cost</div>
                    <div className={`cmp-npc ${isLowest && ranked.length > 1 ? 'is-best' : ''} ${isHighest ? 'is-worst' : ''}`}>{formatUSD(r.npc)}</div>
                    <div className="cmp-npc-note">
                      {isLowest && ranked.length > 1 && '✓ Lowest present cost'}
                      {isHighest && ranked.length > 1 && '✗ Highest present cost'}
                    </div>
                  </div>
                </div>
                <div className="cmp-score-row">
                  <div className="cmp-score-label">Internal Rate of Return</div>
                  <div className={`cmp-score-val ${irrBeatsRate ? 'is-good' : ''}`}>{formatPct(r.irr)}</div>
                </div>
                <div className="cmp-totals">
                  <div className="cmp-totals-row"><span>Total premiums paid</span><span>{formatUSD(r.totalCost)}</span></div>
                  <div className="cmp-totals-row"><span>Withdrawal at maturity</span><span>{formatUSD(r.withdrawal)}</span></div>
                  {r.totalInflows > 0 && <div className="cmp-totals-row"><span>Accumulated savings</span><span>{formatUSD(r.totalInflows)}</span></div>}
                </div>
              </div>
            </div>
          );
        })}
      </div>

      <div className="cmp-card">
        <div className="cmp-card-head">Ranking by NPC (lower is better)</div>
        <div className="cmp-table-wrap">
          <table className="cmp-table">
            <thead>
              <tr>
                <th>#</th><th>Plan</th>
                <th className="t-right">Annual Premium</th><th className="t-right">Years</th>
                <th className="t-right">Withdrawal</th><th className="t-right">NPC</th>
                <th className="t-right">IRR</th><th className="t-center">Verdict</th>
              </tr>
            </thead>
            <tbody>
              {ranked.map((r, rank) => {
                const v = verdictMap[r.idx];
                const isFirst = rank === 0;
                const isLast = rank === ranked.length - 1 && minNPC !== maxNPC;
                return (
                  <tr key={r.idx} className={isFirst ? 'is-first' : ''}>
                    <td><span className={`cmp-rank-pill ${isFirst ? 'is-first' : ''}`}>{rank + 1}</span></td>
                    <td className="cmp-cell-name">{r.name}</td>
                    <td className="t-right">{formatUSD(r.premium)}</td>
                    <td className="t-right">{r.years}</td>
                    <td className="t-right">{formatUSD(r.withdrawal)}</td>
                    <td className={`t-right ${isFirst ? 'cmp-cell-npc-best' : (isLast ? 'cmp-cell-npc-worst' : 'cmp-cell-npc')}`}>{formatUSD(r.npc)}</td>
                    <td className="t-right">{formatPct(r.irr)}</td>
                    <td className="t-center"><span className={`cmp-badge ${v.cls}`}>{v.label}</span></td>
                  </tr>
                );
              })}
            </tbody>
          </table>
        </div>
      </div>

      <div className="cmp-card">
        <div className="cmp-card-pad">
          <h3 className="cmp-section-title">Decision guide</h3>
          <div className="cmp-guide">
            <div className="cmp-guide-item cmp-guide-green"><strong>Lowest NPC</strong>The most efficient policy in terms of net present cost — the option to recommend when coverage is identical.</div>
            <div className="cmp-guide-item cmp-guide-slate"><strong>Similar NPCs (&lt; 5%)</strong>Consider non-financial factors: insurer strength, service quality, contract exclusions.</div>
            <div className="cmp-guide-item cmp-guide-gold"><strong>IRR &gt; discount rate</strong>The return component beats the client's opportunity cost.</div>
            <div className="cmp-guide-item cmp-guide-red"><strong>Very low or negative IRR</strong>The client is paying mostly for coverage, not return. Normal and acceptable if they need the protection.</div>
          </div>
        </div>
      </div>

      <div className="cmp-tool-foot">Comparative financial analysis · Constant insured amount across policies</div>
    </div>
  );
}

ReactDOM.createRoot(document.getElementById('comparador-root')).render(<App />);
