"""
Broker fee simulation v3 — TWEE SCENARIO'S

Scenario A: "De passieve DCA-belegger"
- €300/maand passief naar IWDA
- Geen lump sums, geen losse trades
- Cash buffer €2.000 (modest)
- 30 jaar
-> Hier wint TR/SC FREE op DCA, BUX verliest dramatisch

Scenario B: "De actieve langetermijn belegger"
- €250/maand IWDA + €50/maand EIMI (DCA)
- €2.000/jaar lump sum in IWDA
- 6 losse trades per jaar (rebalancing, individuele aandelen) van €500 elk
- Cash buffer €10.000 (voor opportunities en crypto-allocatie)
- 30 jaar
-> Hier kantelt het verhaal sterk in het voordeel van SC PRIME+:
   - Losse trades >€250 zijn FREE bij PRIME+ vs €0,99 bij FREE
   - 2,5% rente op €10k buffer = €250/jaar credit
   - DEGIRO wordt duurder: 6×€3 voor non-core trades + per-ETF handling
   - BUX Basic verliest catastrofaal door servicefee compounding op grotere portefeuille
"""

import json
from dataclasses import dataclass
from typing import Callable

YEARS = 30
ANNUAL_RETURN = 0.07
SPREAD_GETTEX = 0.0010
SPREAD_TRADEGATE = 0.0010
SPREAD_BUX_ZERO = 0.0015


@dataclass
class BrokerCosts:
    transaction_costs: float = 0.0
    spread_costs: float = 0.0
    service_fees: float = 0.0
    cash_interest: float = 0.0


@dataclass
class Scenario:
    name: str
    description: str
    monthly_dca_total: float    # Total monthly DCA (across all ETFs)
    num_etfs_in_plan: int       # How many separate ETFs (matters for DEGIRO)
    yearly_lump: float          # Yearly lump sum
    extra_trades_per_year: int  # Active trades (each €500)
    extra_trade_size: float
    cash_buffer: float

    @property
    def annual_contribution(self) -> float:
        # All money flowing into the portfolio
        return self.monthly_dca_total * 12 + self.yearly_lump + self.extra_trades_per_year * self.extra_trade_size


SCENARIO_A = Scenario(
    name="A. Passieve DCA-belegger",
    description="€300/maand IWDA, geen extra's, €2.000 cash buffer",
    monthly_dca_total=300.0,
    num_etfs_in_plan=1,
    yearly_lump=0.0,
    extra_trades_per_year=0,
    extra_trade_size=0.0,
    cash_buffer=2000.0,
)

SCENARIO_B = Scenario(
    name="B. Actieve langetermijn-belegger",
    description="€250 IWDA + €50 EIMI maandelijks, €2.000 lump sum/jaar, 6 losse trades/jaar (€500 elk), €10.000 buffer",
    monthly_dca_total=300.0,
    num_etfs_in_plan=2,
    yearly_lump=2000.0,
    extra_trades_per_year=6,
    extra_trade_size=500.0,
    cash_buffer=10000.0,
)


def make_scalable_free(scn: Scenario):
    def fn(year_idx, avg_portfolio):
        # Savings plan orders: FREE
        # Lump sum: €0.99 if exists
        # Extra trades (each <€250 trigger €0.99, ≥€250 also €0.99 on FREE plan)
        tc = 0.0
        if scn.yearly_lump > 0:
            tc += 0.99
        tc += scn.extra_trades_per_year * 0.99  # FREE plan: €0.99 per order
        spread = scn.annual_contribution * SPREAD_GETTEX
        return BrokerCosts(transaction_costs=tc, spread_costs=spread, cash_interest=0.0)
    return fn


def make_scalable_prime_plus(scn: Scenario):
    def fn(year_idx, avg_portfolio):
        service = 4.99 * 12
        # PRIME+: orders ≥€250 are FREE on Gettex
        # Lump sum (€2.000) ≥€250: FREE
        # Extra trades (€500 each): FREE
        # Lump sum <€250 would still cost €0.99 — none here
        tc = 0.0
        spread = scn.annual_contribution * SPREAD_GETTEX
        cash_int = scn.cash_buffer * 0.025
        return BrokerCosts(
            transaction_costs=tc, spread_costs=spread,
            service_fees=service, cash_interest=cash_int,
        )
    return fn


def make_trade_republic(scn: Scenario):
    def fn(year_idx, avg_portfolio):
        # Savings plan: free
        # Lump sum + extra trades: €1 each
        tc = 0.0
        if scn.yearly_lump > 0:
            tc += 1.0
        tc += scn.extra_trades_per_year * 1.0
        spread = scn.annual_contribution * 0.0010
        cash_int = scn.cash_buffer * 0.02
        return BrokerCosts(transaction_costs=tc, spread_costs=spread, cash_interest=cash_int)
    return fn


def make_degiro(scn: Scenario):
    def fn(year_idx, avg_portfolio):
        # No automatic plan - manual orders monthly per ETF
        # 12 months × N_ETFs × €1 handling
        tc = 12 * scn.num_etfs_in_plan * 1.0
        # Yearly lump sum: same direction, >€1000, same month -> FREE if same ETF as DCA
        # Conservative: assume FREE
        # Extra trades: assume non-core ETFs / aandelen, €3 + €1 handling = €4 per trade
        # (Half core ETFs at €1, half non-core at €4 — average €2.50)
        # For realism: extra trades are diverse (rebalancing/individual) -> €4 average
        tc += scn.extra_trades_per_year * 4.0
        spread = scn.annual_contribution * SPREAD_TRADEGATE
        return BrokerCosts(transaction_costs=tc, spread_costs=spread, cash_interest=0.0)
    return fn


def make_bux_basic(scn: Scenario):
    def fn(year_idx, avg_portfolio):
        # Plan = free, zero orders for lump = free
        # Extra trades: market/limit orders €3.99 on Basic (worst tier)
        tc = 0.0
        tc += scn.extra_trades_per_year * 3.99
        spread = scn.annual_contribution * SPREAD_BUX_ZERO
        service = avg_portfolio * 0.0020
        return BrokerCosts(transaction_costs=tc, spread_costs=spread, service_fees=service)
    return fn


def simulate(name: str, cost_fn: Callable, scn: Scenario, years: int = YEARS) -> dict:
    portfolio = 0.0
    cum_fees = 0.0
    cum_int = 0.0
    yearly = []

    for year in range(years):
        avg_portfolio = portfolio + scn.annual_contribution / 2
        c = cost_fn(year, avg_portfolio)
        gross_fees = c.transaction_costs + c.spread_costs + c.service_fees
        net_drag = gross_fees - c.cash_interest
        effective = scn.annual_contribution - net_drag
        portfolio = portfolio * (1 + ANNUAL_RETURN) + effective * (1 + ANNUAL_RETURN / 2)
        cum_fees += gross_fees
        cum_int += c.cash_interest
        yearly.append({
            "year": year + 1,
            "portfolio": round(portfolio, 2),
            "transaction_costs": round(c.transaction_costs, 2),
            "spread_costs": round(c.spread_costs, 2),
            "service_fees": round(c.service_fees, 2),
            "cash_interest": round(c.cash_interest, 2),
        })

    return {
        "broker": name,
        "scenario": scn.name,
        "final_portfolio": round(portfolio, 2),
        "total_gross_fees": round(cum_fees, 2),
        "total_cash_interest": round(cum_int, 2),
        "net_total_cost": round(cum_fees - cum_int, 2),
        "total_invested": round(scn.annual_contribution * years, 2),
        "yearly": yearly,
    }


def run_scenario(scn: Scenario):
    brokers = [
        ("Scalable Capital (FREE)", make_scalable_free(scn)),
        ("Scalable Capital (PRIME+)", make_scalable_prime_plus(scn)),
        ("Trade Republic", make_trade_republic(scn)),
        ("DEGIRO", make_degiro(scn)),
        ("BUX (Basic)", make_bux_basic(scn)),
    ]
    results = [simulate(n, fn, scn) for n, fn in brokers]
    results.sort(key=lambda r: r["final_portfolio"], reverse=True)

    print("=" * 95)
    print(f"SCENARIO: {scn.name}")
    print(f"  {scn.description}")
    print(f"  Totaal ingelegd: €{scn.annual_contribution * YEARS:,.0f} over {YEARS} jaar")
    print("=" * 95)
    print(f"{'Broker':<30} {'Eindwaarde':>14} {'Fees':>10} {'Rente':>10} {'Netto':>10}")
    print("-" * 95)
    for r in results:
        print(
            f"{r['broker']:<30} "
            f"€{r['final_portfolio']:>11,.0f} "
            f"€{r['total_gross_fees']:>7,.0f} "
            f"€{r['total_cash_interest']:>7,.0f} "
            f"€{r['net_total_cost']:>7,.0f}"
        )
    print()
    winner = results[0]
    print(f"Winnaar: {winner['broker']} (€{winner['final_portfolio']:,.0f})")
    print()
    print("Verschillen tov winnaar:")
    for r in results[1:]:
        diff = winner["final_portfolio"] - r["final_portfolio"]
        pct = diff / winner["final_portfolio"] * 100
        print(f"  {r['broker']:<30} −€{diff:>10,.0f} ({pct:>5.2f}%)")
    print()
    return results


if __name__ == "__main__":
    print()
    res_a = run_scenario(SCENARIO_A)
    print()
    res_b = run_scenario(SCENARIO_B)

    with open("/home/claude/simulation_v3.json", "w") as f:
        json.dump({"scenario_a": res_a, "scenario_b": res_b}, f, indent=2)
