"""
dashboard_server_japan_patch.py
================================
Patch script — applies Japan/USDJPY additions to dashboard_server.py.

Run from project root:
  python src\execution\dashboard_server_japan_patch.py

Changes made:
  1. TE_JP_SCORES dict       — Japan event scoring (BoJ=90, CPI=80, etc.)
  2. BOJ_MEETINGS_2026       — hardcoded BoJ dates for outlook fallback
  3. _score_te_event()       — recognise Japan events, return JP score
  4. _fetch_te_calendar()    — add Japan to FinanceFlow country list
  5. get_usdjpy_weekly_outlook() — new function, mirrors EU version
  6. _usdjpy_outlook_cache   — separate cache for USDJPY
  7. write_data()            — add usdjpy_outlook + usdjpy data to JSON
"""

from pathlib import Path
import re

BASE_PATH   = Path(__file__).resolve().parents[2]
SERVER_FILE = BASE_PATH / "src" / "execution" / "dashboard_server.py"

def patch():
    code = SERVER_FILE.read_text(encoding="utf-8")
    changes = 0

    # ── PATCH 1: Add Japan scoring dict + BoJ dates after TE_DE_EU_SCORES ────
    JP_BLOCK = '''
# Japan event scores — USDJPY signal drivers
TE_JP_SCORES = {
    "boj rate decision":         90,
    "bank of japan":             90,
    "monetary policy":           88,
    "interest rate decision":    88,
    "boj":                       85,
    "cpi":                       80,
    "consumer price index":      80,
    "inflation":                 75,
    "tankan":                    75,
    "gdp":                       70,
    "gross domestic product":    70,
    "trade balance":             50,
    "current account":           50,
    "retail sales":              45,
    "industrial production":     45,
    "unemployment":              45,
    "pmi":                       40,
    "machine orders":            40,
    "housing starts":            35,
}

# BoJ meeting dates 2026 — hardcoded fallback if FinanceFlow misses them
BOJ_MEETINGS_2026 = [
    "2026-01-23", "2026-03-18", "2026-04-30", "2026-06-16",
    "2026-07-30", "2026-09-17", "2026-10-28", "2026-12-18",
]
'''
    marker = "_outlook_cache = {\"data\": None, \"fetched\": None}"
    if "TE_JP_SCORES" not in code:
        code = code.replace(marker, JP_BLOCK + "\n" + marker)
        changes += 1
        print("  ✅ Patch 1: Added TE_JP_SCORES + BOJ_MEETINGS_2026")
    else:
        print("  ⏭  Patch 1: Already applied")

    # ── PATCH 2: Add separate USDJPY outlook cache ─────────────────────────
    uj_cache_marker = '_outlook_cache = {"data": None, "fetched": None}'
    uj_cache_new    = '_outlook_cache = {"data": None, "fetched": None}\n_usdjpy_outlook_cache = {"data": None, "fetched": None}'
    if "_usdjpy_outlook_cache" not in code:
        code = code.replace(uj_cache_marker, uj_cache_new)
        changes += 1
        print("  ✅ Patch 2: Added _usdjpy_outlook_cache")
    else:
        print("  ⏭  Patch 2: Already applied")

    # ── PATCH 3: Update _score_te_event to handle Japan ───────────────────
    old_score = '''    is_us = "united states" in country
    is_de = "germany" in country
    is_eu = "euro" in country

    if not (is_us or is_de or is_eu):
        return 0

    # Exclude noise events regardless of importance
    if any(excl in category for excl in TE_EXCLUSIONS):
        return 0

    scores = TE_US_SCORES if is_us else TE_DE_EU_SCORES
    base   = max((v for k,v in scores.items() if k in category), default=0)

    # Only use importance fallback for Importance=3 (High) events not matched
    if base == 0 and importance == 3:
        base = 35
    # Cap ECB non-policy events
    if is_eu and base > 0 and "non-monetary" in category:
        base = 5
    return base'''

    new_score = '''    is_us = "united states" in country
    is_de = "germany" in country
    is_eu = "euro" in country
    is_jp = "japan" in country

    if not (is_us or is_de or is_eu or is_jp):
        return 0

    # Exclude noise events regardless of importance
    if any(excl in category for excl in TE_EXCLUSIONS):
        return 0

    if is_jp:
        scores = TE_JP_SCORES
    elif is_us:
        scores = TE_US_SCORES
    else:
        scores = TE_DE_EU_SCORES
    base = max((v for k, v in scores.items() if k in category), default=0)

    # Only use importance fallback for Importance=3 (High) events not matched
    if base == 0 and importance == 3:
        base = 35
    # Cap ECB non-policy events
    if is_eu and base > 0 and "non-monetary" in category:
        base = 5
    return base'''

    if "is_jp" not in code:
        code = code.replace(old_score, new_score)
        changes += 1
        print("  ✅ Patch 3: Updated _score_te_event for Japan")
    else:
        print("  ⏭  Patch 3: Already applied")

    # ── PATCH 4: Add Japan to FinanceFlow country list ────────────────────
    old_countries = '    for country_name, label in [("United States","US"),("Germany","DE"),("Euro Area","EU")]:'
    new_countries = '    for country_name, label in [("United States","US"),("Germany","DE"),("Euro Area","EU"),("Japan","JP")]:'
    if '"Japan","JP"' not in code:
        code = code.replace(old_countries, new_countries)
        changes += 1
        print("  ✅ Patch 4: Added Japan to FinanceFlow fetch")
    else:
        print("  ⏭  Patch 4: Already applied")

    # ── PATCH 5: Add get_usdjpy_weekly_outlook() function ─────────────────
    UJ_OUTLOOK_FN = '''

def get_usdjpy_weekly_outlook(current_z: float = 0.0) -> list:
    """
    Build USDJPY weekly signal outlook.
    Mirrors get_weekly_outlook() but uses:
      - Japan events from FinanceFlow (BoJ, CPI, GDP, Tankan)
      - FOMC events (affect USD side of US-JP spread)
      - Threshold ±2.0 for proximity scoring
      - BOJ_MEETINGS_2026 as hardcoded fallback
    """
    cache     = _usdjpy_outlook_cache
    now       = datetime.now()
    cache_age = (now - cache["fetched"]).total_seconds() if cache["fetched"] else 99999

    if cache["data"] and cache_age < 21600:   # 6-hour cache
        data = cache["data"]
        return _recalc_uj_probs(data, current_z)

    monday = now - timedelta(days=now.weekday())
    start  = monday.strftime("%Y-%m-%d")
    end    = (monday + timedelta(days=6)).strftime("%Y-%m-%d")

    # Build week_events dict from FinanceFlow (Japan + US FOMC events only)
    week_events = {(monday + timedelta(days=i)).strftime("%Y-%m-%d"): []
                   for i in range(5)}

    te_events = _fetch_te_calendar(start, end)
    for e in te_events:
        country = e.get("Country", "").lower()
        # Only include Japan events and US events (FOMC/NFP/CPI affect USD)
        if "japan" not in country and "united states" not in country:
            continue
        score = _score_te_event(e)
        if score <= 0:
            continue
        date = e.get("Date", "")[:10]
        if date in week_events:
            week_events[date].append({
                "event":   e.get("Event", e.get("Name", "")),
                "country": e.get("Country", ""),
                "score":   score,
                "time":    e.get("Time", ""),
            })

    # Hardcoded BoJ fallback
    for date in week_events:
        if date in BOJ_MEETINGS_2026:
            if not any("boj" in ev["event"].lower() or "bank of japan" in ev["event"].lower()
                       for ev in week_events[date]):
                week_events[date].insert(0, {
                    "event": "BoJ Rate Decision", "country": "Japan",
                    "score": 90, "source": "hardcoded",
                })

    days   = ["Mon", "Tue", "Wed", "Thu", "Fri"]
    result = []
    for i in range(5):
        date = (monday + timedelta(days=i)).strftime("%Y-%m-%d")
        evs  = sorted(week_events.get(date, []),
                      key=lambda x: x["score"], reverse=True)
        top  = evs[0]["score"] if evs else 0

        # Proximity contribution — how close is z to ±2.0 threshold
        dist = 2.0 - abs(current_z)
        zp   = (35 if dist <= 0 else 28 if dist < 0.5 else 18
                if dist < 1.0 else 10 if dist < 1.5 else 5)

        ep   = min(top / 100 * 45, 45)
        prob = min(int(ep + zp), 90)

        result.append({
            "date":      date,
            "day":       days[i],
            "events":    evs[:3],
            "prob":      prob,
            "top_event": evs[0]["event"] if evs else "No key events",
        })

    cache["data"]    = result
    cache["fetched"] = now
    return result


def _recalc_uj_probs(days_data: list, current_z: float) -> list:
    """Recalculate USDJPY signal probabilities with fresh z-score."""
    dist = 2.0 - abs(current_z)
    zp   = (35 if dist <= 0 else 28 if dist < 0.5 else 18
            if dist < 1.0 else 10 if dist < 1.5 else 5)
    result = []
    for d in days_data:
        top  = d["events"][0]["score"] if d["events"] else 0
        ep   = min(top / 100 * 45, 45)
        prob = min(int(ep + zp), 90)
        result.append({**d, "prob": prob})
    return result

'''
    if "get_usdjpy_weekly_outlook" not in code:
        # Insert before write_data()
        code = code.replace("def write_data():", UJ_OUTLOOK_FN + "def write_data():")
        changes += 1
        print("  ✅ Patch 5: Added get_usdjpy_weekly_outlook()")
    else:
        print("  ⏭  Patch 5: Already applied")

    # ── PATCH 6: Update write_data() to include USDJPY data ───────────────
    old_write = '''        data["yields"] = fetch_yield_data()
        DATA_FILE.write_text(json.dumps(data, indent=2), encoding="utf-8")
        z   = data["zscore"]
        sig = "SIGNAL" if abs(z) >= 2.75 else "no signal"
        print(f"  [{datetime.now().strftime('%H:%M:%S')}] "
              f"z={z:.3f} | {sig} | "
              f"trades={data['trade_count']} | "
              f"pos={data['position']}")'''

    new_write = '''        data["yields"] = fetch_yield_data()

        # ── USDJPY additions ───────────────────────────────────────────────
        try:
            from execution.dashboard_server_v2_additions import (
                fetch_usdjpy_yield_data, parse_usdjpy_trades,
                combined_portfolio_stats,
            )
            data["usdjpy"]        = fetch_usdjpy_yield_data()
            data["usdjpy_trades"] = parse_usdjpy_trades()
            data["portfolio"]     = combined_portfolio_stats()
        except ImportError:
            pass  # additions file not yet deployed — skip gracefully

        # USDJPY signal outlook
        try:
            uj_z = 0.0
            if isinstance(data.get("usdjpy"), dict):
                uj_z = float(data["usdjpy"].get("current_z", 0.0))
            data["usdjpy_outlook"] = get_usdjpy_weekly_outlook(uj_z)
        except Exception as _e:
            data["usdjpy_outlook"] = []

        DATA_FILE.write_text(json.dumps(data, indent=2), encoding="utf-8")
        z    = data["zscore"]
        uj_z = data.get("usdjpy", {}).get("current_z", 0.0) if isinstance(data.get("usdjpy"), dict) else 0.0
        eu_sig = "SIGNAL" if abs(z) >= 2.75 else "no signal"
        uj_sig = "SIGNAL" if abs(uj_z) >= 2.00 else "no signal"
        print(f"  [{datetime.now().strftime('%H:%M:%S')}] "
              f"EU z={z:.3f} {eu_sig} | "
              f"UJ z={uj_z:.3f} {uj_sig} | "
              f"trades={data['trade_count']} | "
              f"pos={data['position']}")'''

    if "USDJPY additions" not in code:
        code = code.replace(old_write, new_write)
        changes += 1
        print("  ✅ Patch 6: Updated write_data() with USDJPY + outlook")
    else:
        print("  ⏭  Patch 6: Already applied")

    # ── Write patched file ─────────────────────────────────────────────────
    if changes > 0:
        # Syntax check before writing
        import ast
        try:
            ast.parse(code)
        except SyntaxError as e:
            print(f"  ❌ Syntax error in patched code: {e}")
            return False

        SERVER_FILE.write_text(code, encoding="utf-8")
        print(f"\n  ✅ dashboard_server.py patched ({changes} changes applied)")
        print(f"  File: {SERVER_FILE}")
    else:
        print("\n  ✅ All patches already applied — no changes needed")

    return True


if __name__ == "__main__":
    print("="*60)
    print("DASHBOARD SERVER — Japan/USDJPY Patch")
    print("="*60)
    ok = patch()
    if ok:
        print("\n  Restart dashboard_server.py to apply changes.")
        print("  Expected new output line:")
        print("  [HH:MM:SS] EU z=X.XXX no signal | UJ z=X.XXX no signal | ...")
        print("\n  FinanceFlow will now fetch Japan events alongside US/DE/EU.")
        print("  You should see: 'FinanceFlow JP: N events' in the output.")
