#!/usr/bin/env python3
"""
Finance Dashboard Generator
Pulls live data from YNAB, Google Sheets, BTC price
Outputs HTML to workspace/www/finance.html
"""

import json, os, time, base64, urllib.request, urllib.parse
from datetime import datetime, timezone
from pathlib import Path

# ── helpers ──────────────────────────────────────────────────────────────────

def get_sheets_token():
    key_file = os.path.expanduser("~/.openclaw/secrets/google-calendar-sa.json")
    sa = json.load(open(key_file))
    now = int(time.time())
    header = base64.urlsafe_b64encode(json.dumps({"alg":"RS256","typ":"JWT"}).encode()).rstrip(b'=').decode()
    payload = base64.urlsafe_b64encode(json.dumps({
        "iss": sa["client_email"],
        "scope": "https://www.googleapis.com/auth/spreadsheets.readonly",
        "aud": "https://oauth2.googleapis.com/token",
        "iat": now, "exp": now+3600
    }).encode()).rstrip(b'=').decode()
    from cryptography.hazmat.primitives import serialization, hashes
    from cryptography.hazmat.primitives.asymmetric import padding
    from cryptography.hazmat.backends import default_backend
    key = serialization.load_pem_private_key(sa["private_key"].encode(), password=None, backend=default_backend())
    sig = key.sign(f"{header}.{payload}".encode(), padding.PKCS1v15(), hashes.SHA256())
    jwt = f"{header}.{payload}.{base64.urlsafe_b64encode(sig).rstrip(b'=').decode()}"
    data = f"grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&assertion={jwt}".encode()
    return json.loads(urllib.request.urlopen(urllib.request.Request("https://oauth2.googleapis.com/token", data=data)).read())["access_token"]

def fetch(url, headers={}):
    req = urllib.request.Request(url, headers=headers)
    return json.loads(urllib.request.urlopen(req, timeout=10).read())

def fmt_cad(n):
    if n is None: return "—"
    neg = n < 0
    s = f"${abs(n):,.0f}"
    return f"-{s}" if neg else s

def parse_currency(s):
    if not s or not str(s).strip(): return 0
    clean = str(s).replace("$","").replace(",","").replace(" ","").strip()
    try: return float(clean)
    except: return 0

# ── data sources ──────────────────────────────────────────────────────────────

def get_btc_price_cad():
    try:
        data = fetch("https://api.coingecko.com/api/v3/simple/price?ids=bitcoin&vs_currencies=cad")
        return data["bitcoin"]["cad"]
    except: return 91301  # fallback

def get_ynab_data():
    key = open(os.path.expanduser("~/.openclaw/secrets/ynab-api-key.txt")).read().strip()
    budget_id = "6c0a7a3f-6fa2-4729-9b88-d6ec3447fa69"
    biz_budget_id = "6b38a734-c3a4-4efd-bdfb-9d317f896711"
    h = {"Authorization": f"Bearer {key}"}

    accts = fetch(f"https://api.ynab.com/v1/budgets/{budget_id}/accounts", h)["data"]["accounts"]
    biz_accts = fetch(f"https://api.ynab.com/v1/budgets/{biz_budget_id}/accounts", h)["data"]["accounts"]

    def bal(accts, acct_id):
        for a in accts:
            if a["id"] == acct_id:
                return a["balance"] / 1000
        return 0

    # Personal accounts
    chq_5322 = bal(accts, "3deb48de-f487-4f05-b268-3bbfa8607060")
    chq_5421 = bal(accts, "e5ea972e-3c66-4e46-95f5-7e7d8c503b74")
    amex      = bal(accts, "9568661e-a80e-4c13-9e49-a5b84f7427d1")
    td_visa   = bal(accts, "072af67d-f487-4f05-b268-3bbfa8607060")
    rbc_loc   = bal(accts, "35745d52-f487-4f05-b268-3bbfa8607060")
    volvo     = bal(accts, "06700e9c-f487-4f05-b268-3bbfa8607060")
    bronco    = bal(accts, "b38d40f0-f487-4f05-b268-3bbfa8607060")

    # Business accounts
    biz_chq   = next((a["balance"]/1000 for a in biz_accts if "9694" in a.get("name","")), 0)
    biz_loc   = next((a["balance"]/1000 for a in biz_accts if "8945" in a.get("name","")), 0)
    biz_amex  = next((a["balance"]/1000 for a in biz_accts if "2006" in a.get("name","")), 0)
    scotialine= next((a["balance"]/1000 for a in biz_accts if "ScotiaLine" in a.get("name","")), 0)

    return {
        "chq_5322": chq_5322, "chq_5421": chq_5421,
        "amex": amex, "td_visa": td_visa, "rbc_loc": rbc_loc,
        "volvo": volvo, "bronco": bronco,
        "biz_chq": biz_chq, "biz_loc": biz_loc,
        "biz_amex": biz_amex, "scotialine": scotialine,
    }

def get_sheet_data():
    token = get_sheets_token()
    sheet_id = "190gcG6GtyPRBFRPNZoy9sysjTnnfKrdeqjPDZSS5rZQ"
    h = {"Authorization": f"Bearer {token}"}
    rows = fetch(f"https://sheets.googleapis.com/v4/spreadsheets/{sheet_id}/values/Networth!A1:Z60", h).get("values", [])

    def last_val(row):
        """Get last non-empty value from a data row (skip col 0 label, col 1 sublabel)"""
        vals = [parse_currency(c) for c in row[2:] if str(c).strip() and parse_currency(c) != 0]
        return vals[-1] if vals else 0

    data = {}
    for row in rows:
        if len(row) < 2: continue
        label = row[1] if len(row) > 1 else row[0]
        v = last_val(row)
        data[label] = v

    return data

# ── build HTML ────────────────────────────────────────────────────────────────

def build_dashboard(ynab, sheet, btc_price):
    ts = datetime.now(timezone.utc).astimezone().strftime("%B %d, %Y at %I:%M %p %Z")

    # BTC holdings
    btc_personal = 0  # update when Mike provides
    btc_business = 0.30224196
    btc_total_cad = (btc_personal + btc_business) * btc_price

    # Assets
    cash_personal = max(0, ynab["chq_5322"]) + max(0, ynab["chq_5421"])
    cash_biz = max(0, ynab["biz_chq"])

    investments_mike = (
        sheet.get("Stocks - Mike Wealthsimple Personal", 0) +
        sheet.get("Stocks - Mike Wealthsimple RRSP", 0) +
        sheet.get("Stocks - Mike Wealthsimple TFSA", 0) +
        sheet.get("Mike Wealthsimple TFSA", 0) +
        sheet.get("Mike Wealthsimple TFSA Emergency Fund", 0) +
        sheet.get("Mike IBM Lira (WealthSimple)", 0) +
        sheet.get("Mike RRSP (WealthSimple)", 0)
    )
    investments_nicola = (
        sheet.get("Nicola Wealthsimple RRSP", 0) +
        sheet.get("Nicola IBM Pension (LIRA Wealthsimple)", 0) +
        sheet.get("Nicola Wealthsimple TFSA", 0)
    )
    resp = sheet.get("Kids RESP (WealthSimple)", 0)
    nicola_pension = sheet.get("Nicola Sunlife KC Pension ", 0) or sheet.get("Nicola Sunlife KC Pension", 0)
    kc_shares = sheet.get("Kimberly Clark Shares", 0)
    home_value = sheet.get("Personal residence", 0) or 2000000
    vehicles = sheet.get("Vehicles", 0) or 20000
    crypto = btc_total_cad

    total_assets = (cash_personal + cash_biz + investments_mike + investments_nicola +
                    resp + nicola_pension + kc_shares + crypto + home_value + vehicles)

    # Debts
    mortgage = sheet.get("Home mortgage", 0) or 1375321
    cc_amex = abs(min(0, ynab["amex"]))
    cc_td = abs(min(0, ynab["td_visa"]))
    rbc_loc = abs(min(0, ynab["rbc_loc"]))
    car_volvo = abs(min(0, ynab["volvo"]))
    car_bronco = abs(min(0, ynab["bronco"]))
    biz_loc = abs(min(0, ynab["biz_loc"]))
    biz_amex = abs(min(0, ynab["biz_amex"]))
    scotialine = abs(min(0, ynab["scotialine"]))

    total_debt = mortgage + cc_amex + cc_td + rbc_loc + car_volvo + car_bronco + biz_loc + biz_amex + scotialine
    net_worth = total_assets - total_debt

    # Debt payoff tracker (avalanche order)
    cc_total = cc_amex + cc_td
    cc_target = 42000  # approx peak
    cc_pct = max(0, min(100, (1 - cc_total/cc_target)*100))

    html = f"""<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Mike & Nicola — Financial Dashboard</title>
<style>
  * {{ box-sizing: border-box; margin: 0; padding: 0; }}
  body {{ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; background: #0f1117; color: #e2e8f0; min-height: 100vh; }}
  .header {{ background: linear-gradient(135deg, #1a1f2e 0%, #0f1117 100%); padding: 2rem; border-bottom: 1px solid #2d3748; }}
  .header h1 {{ font-size: 1.4rem; color: #a0aec0; font-weight: 400; }}
  .header .net-worth {{ font-size: 3rem; font-weight: 700; color: {'#68d391' if net_worth >= 0 else '#fc8181'}; margin: 0.5rem 0; }}
  .header .updated {{ font-size: 0.8rem; color: #4a5568; margin-top: 0.5rem; }}
  .grid {{ display: grid; grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); gap: 1rem; padding: 1.5rem; }}
  .card {{ background: #1a1f2e; border: 1px solid #2d3748; border-radius: 12px; padding: 1.5rem; }}
  .card h2 {{ font-size: 0.75rem; text-transform: uppercase; letter-spacing: 0.1em; color: #4a5568; margin-bottom: 1rem; }}
  .row {{ display: flex; justify-content: space-between; align-items: center; padding: 0.4rem 0; border-bottom: 1px solid #2d3748; }}
  .row:last-child {{ border-bottom: none; }}
  .row .label {{ color: #a0aec0; font-size: 0.9rem; }}
  .row .val {{ font-weight: 600; font-size: 0.95rem; }}
  .val.green {{ color: #68d391; }}
  .val.red {{ color: #fc8181; }}
  .val.yellow {{ color: #f6e05e; }}
  .val.blue {{ color: #63b3ed; }}
  .total-row {{ background: #2d3748; border-radius: 6px; padding: 0.6rem 0.75rem; margin-top: 0.75rem; display: flex; justify-content: space-between; font-weight: 700; }}
  .note {{ font-size: 0.75rem; color: #4a5568; margin-top: 0.5rem; font-style: italic; }}
  .progress-bar {{ background: #2d3748; border-radius: 99px; height: 8px; margin-top: 0.5rem; overflow: hidden; }}
  .progress-fill {{ height: 100%; border-radius: 99px; background: linear-gradient(90deg, #68d391, #38a169); transition: width 0.3s; }}
  .debt-stage {{ margin-bottom: 1rem; }}
  .debt-stage .stage-label {{ font-size: 0.8rem; color: #a0aec0; margin-bottom: 0.25rem; }}
  .debt-stage .stage-info {{ display: flex; justify-content: space-between; font-size: 0.85rem; }}
  .badge {{ font-size: 0.65rem; padding: 2px 6px; border-radius: 99px; font-weight: 600; }}
  .badge.active {{ background: #2c7a3a; color: #68d391; }}
  .badge.next {{ background: #2d3748; color: #4a5568; }}
  .stale-note {{ background: #2a1f0f; border: 1px solid #744210; border-radius: 8px; padding: 0.75rem; margin-top: 0.75rem; font-size: 0.8rem; color: #f6ad55; }}
</style>
</head>
<body>
<div class="header">
  <h1>Mike & Nicola — Net Worth</h1>
  <div class="net-worth">{fmt_cad(net_worth)}</div>
  <div class="updated">Last updated {ts}</div>
</div>

<div class="grid">

  <!-- CASH -->
  <div class="card">
    <h2>💵 Cash & Banking</h2>
    <div class="row"><span class="label">Joint Chequing 5322</span><span class="val green">{fmt_cad(ynab['chq_5322'])}</span></div>
    <div class="row"><span class="label">Joint Chequing 5421</span><span class="val green">{fmt_cad(ynab['chq_5421'])}</span></div>
    <div class="row"><span class="label">NMC Business Chequing</span><span class="val green">{fmt_cad(ynab['biz_chq'])}</span></div>
    <div class="total-row"><span>Total Cash</span><span class="green">{fmt_cad(cash_personal + cash_biz)}</span></div>
  </div>

  <!-- MIKE INVESTMENTS -->
  <div class="card">
    <h2>📈 Mike's Investments</h2>
    <div class="row"><span class="label">Wealthsimple RRSP</span><span class="val blue">{fmt_cad(sheet.get("Mike RRSP (WealthSimple)", 0))}</span></div>
    <div class="row"><span class="label">Wealthsimple TFSA</span><span class="val blue">{fmt_cad(sheet.get("Mike Wealthsimple TFSA", 0) + sheet.get("Mike Wealthsimple TFSA Emergency Fund", 0))}</span></div>
    <div class="row"><span class="label">IBM LIRA (Wealthsimple)</span><span class="val blue">{fmt_cad(sheet.get("Mike IBM Lira (WealthSimple)", 0))}</span></div>
    <div class="row"><span class="label">Personal (Wealthsimple)</span><span class="val blue">{fmt_cad(sheet.get("Stocks - Mike Wealthsimple Personal", 0))}</span></div>
    <div class="total-row"><span>Total</span><span class="blue">{fmt_cad(investments_mike)}</span></div>
    <p class="note">⚠️ Values from Dec 2025 sheet — update Wealthsimple balances</p>
  </div>

  <!-- NICOLA INVESTMENTS -->
  <div class="card">
    <h2>📈 Nicola's Investments</h2>
    <div class="row"><span class="label">Sun Life KC Pension</span><span class="val blue">{fmt_cad(nicola_pension)}</span></div>
    <div class="row"><span class="label">Wealthsimple RRSP</span><span class="val blue">{fmt_cad(sheet.get("Nicola Wealthsimple RRSP", 0))}</span></div>
    <div class="row"><span class="label">IBM LIRA (Wealthsimple)</span><span class="val blue">{fmt_cad(sheet.get("Nicola IBM Pension (LIRA Wealthsimple)", 0))}</span></div>
    <div class="row"><span class="label">Kimberly Clark Shares</span><span class="val blue">{fmt_cad(kc_shares)}</span></div>
    <div class="total-row"><span>Total</span><span class="blue">{fmt_cad(investments_nicola + nicola_pension + kc_shares)}</span></div>
    <p class="note">⚠️ Values from Dec 2025 sheet — update Wealthsimple balances</p>
  </div>

  <!-- KIDS & CRYPTO -->
  <div class="card">
    <h2>👨‍👩‍👧‍👦 Kids & Crypto</h2>
    <div class="row"><span class="label">RESP (Ben & Ellie)</span><span class="val green">{fmt_cad(resp)}</span></div>
    <div class="row"><span class="label">BTC Business (0.302 BTC)</span><span class="val yellow">{fmt_cad(btc_total_cad)}</span></div>
    <div class="row"><span class="label">BTC Price (CAD)</span><span class="val yellow">{fmt_cad(btc_price)}</span></div>
    <p class="note">⚠️ RESP from Dec 2025 sheet — update monthly. Personal BTC unknown.</p>
  </div>

  <!-- REAL ESTATE -->
  <div class="card">
    <h2>🏠 Real Estate & Vehicles</h2>
    <div class="row"><span class="label">Primary Residence</span><span class="val green">{fmt_cad(home_value)}</span></div>
    <div class="row"><span class="label">Vehicles (est.)</span><span class="val green">{fmt_cad(vehicles)}</span></div>
    <div class="total-row"><span>Total</span><span class="green">{fmt_cad(home_value + vehicles)}</span></div>
    <p class="note">⚠️ Home value last updated Dec 2025</p>
  </div>

  <!-- DEBT AVALANCHE -->
  <div class="card">
    <h2>🎯 Debt Payoff — Avalanche Order</h2>

    <div class="debt-stage">
      <div class="stage-info">
        <span class="label">🔴 STAGE 1 — Credit Cards (19%)</span>
        <span class="badge active">ACTIVE</span>
      </div>
      <div class="row" style="margin-top:0.5rem"><span class="label">Amex Personal</span><span class="val red">{fmt_cad(cc_amex)}</span></div>
      <div class="row"><span class="label">TD Visa</span><span class="val red">{fmt_cad(cc_td)}</span></div>
      <div class="row"><span class="label">Amex Business</span><span class="val red">{fmt_cad(biz_amex)}</span></div>
      <div class="total-row"><span>Total CC Debt</span><span class="red">{fmt_cad(cc_amex + cc_td + biz_amex)}</span></div>
      <div class="progress-bar"><div class="progress-fill" style="width:{cc_pct:.0f}%"></div></div>
      <div style="font-size:0.75rem;color:#4a5568;margin-top:0.25rem">{cc_pct:.0f}% paid off</div>
    </div>

    <div class="debt-stage">
      <div class="stage-info">
        <span class="label">🟡 STAGE 2 — Unsecured LOCs (9-10%)</span>
        <span class="badge next">NEXT</span>
      </div>
      <div class="row" style="margin-top:0.5rem"><span class="label">Unsecured LOC 8945</span><span class="val yellow">{fmt_cad(biz_loc)}</span></div>
      <div class="row"><span class="label">RBC Credit Line</span><span class="val yellow">{fmt_cad(rbc_loc)}</span></div>
      <div class="row"><span class="label">ScotiaLine</span><span class="val yellow">{fmt_cad(scotialine)}</span></div>
    </div>

    <div class="debt-stage">
      <div class="stage-info">
        <span class="label">🚗 Car Loans (Volvo 9.99%, Bronco 3.49%)</span>
        <span class="badge next">LATER</span>
      </div>
      <div class="row" style="margin-top:0.5rem"><span class="label">Volvo XC90</span><span class="val yellow">{fmt_cad(car_volvo)}</span></div>
      <div class="row"><span class="label">Ford Bronco</span><span class="val yellow">{fmt_cad(car_bronco)}</span></div>
    </div>

    <div class="debt-stage">
      <div class="stage-info">
        <span class="label">🏠 Mortgage</span>
        <span class="badge next">LAST</span>
      </div>
      <div class="row" style="margin-top:0.5rem"><span class="label">Home Mortgage</span><span class="val red">{fmt_cad(mortgage)}</span></div>
    </div>

    <div class="total-row" style="margin-top:1rem"><span>Total Debt</span><span class="red">{fmt_cad(total_debt)}</span></div>
  </div>

  <!-- SUMMARY -->
  <div class="card">
    <h2>📊 Summary</h2>
    <div class="row"><span class="label">Total Assets</span><span class="val green">{fmt_cad(total_assets)}</span></div>
    <div class="row"><span class="label">Total Debt</span><span class="val red">{fmt_cad(total_debt)}</span></div>
    <div class="row"><span class="label">Net Worth</span><span class="val {'green' if net_worth >= 0 else 'red'}">{fmt_cad(net_worth)}</span></div>
    <div class="row"><span class="label">Excl. Home</span><span class="val {'green' if (net_worth - home_value) >= 0 else 'red'}">{fmt_cad(net_worth - home_value)}</span></div>
    <div class="stale-note">⚠️ Investment values are from Dec 2025. Update Wealthsimple, RESP, and pension balances to get accurate net worth.</div>
  </div>

</div>
</body>
</html>"""
    return html

# ── main ──────────────────────────────────────────────────────────────────────

if __name__ == "__main__":
    print("Fetching data...")
    btc = get_btc_price_cad()
    print(f"BTC: ${btc:,.0f} CAD")
    ynab = get_ynab_data()
    print(f"YNAB loaded")
    sheet = get_sheet_data()
    print(f"Sheet loaded: {len(sheet)} rows")

    html = build_dashboard(ynab, sheet, btc)
    out = Path(os.path.expanduser("~/.openclaw/workspace/www/finance.html"))
    out.parent.mkdir(exist_ok=True)
    out.write_text(html)
    print(f"Dashboard written to {out}")
