"""
day_report.py -- [2026-09-28] One-symbol, one-day report section (markdown +
chart): premarket-to-close 1-min chart with VWAP and the bot's trades, price
vs open / VWAP through the day, volume by hour, the bot's scanner ranks, its
entry-check decisions and its trades that day.

    python3 day_report.py AGEN 2026-09-21 2026-09-22 ...   -> <SYM>_<date>.md + .png per day
"""
import csv
import glob
import json
import re
import sys
from collections import Counter
from datetime import datetime, timedelta
from pathlib import Path

import matplotlib
matplotlib.use("Agg")
import matplotlib.dates as md
import matplotlib.pyplot as plt

HERE = Path(__file__).resolve().parent
TRADE = Path("/var/www/screener/trade")
sys.path.insert(0, str(TRADE))


def load_minutes(sym, day):
    p = HERE / f"{sym}_1min_premarket_to_close_6days.csv"
    rows = [r for r in csv.DictReader(open(p)) if r["date"] == day] if p.exists() else []
    if rows:
        return rows
    from alpaca_client import get_client
    from zoneinfo import ZoneInfo
    ET = ZoneInfo("America/New_York")
    d = datetime.fromisoformat(day).replace(tzinfo=ET)
    pv = v = 0.0
    out = []
    for b in get_client().get_minute_bars(sym, start=d.replace(hour=4), end=d.replace(hour=16), limit=1000):
        t = b.timestamp.astimezone(ET)
        sess = "pre" if t.strftime("%H:%M") < "09:30" else "reg"
        if sess == "reg":
            pv += (float(b.high) + float(b.low) + float(b.close)) / 3 * float(b.volume)
            v += float(b.volume)
        out.append({"date": day, "time_et": t.strftime("%H:%M"), "session": sess, "open": b.open, "high": b.high,
                    "low": b.low, "close": b.close, "volume": b.volume, "vwap_regular": round(pv / v, 4) if v else ""})
    return out


def news_for(sym, day, days_before=3):
    """Alpaca news (Benzinga) mentioning sym, from days_before days before `day`
    through its 4:00 PM close. [(time ET, headline, source, url)]"""
    import requests
    from zoneinfo import ZoneInfo
    from config_loader import get_env
    ET = ZoneInfo("America/New_York")
    e = get_env()
    d = datetime.fromisoformat(day).replace(tzinfo=ET)
    r = requests.get("https://data.alpaca.markets/v1beta1/news",
                     headers={"APCA-API-KEY-ID": e.api_key, "APCA-API-SECRET-KEY": e.secret_key},
                     params={"symbols": sym, "start": (d - timedelta(days=days_before)).isoformat(),
                             "end": d.replace(hour=16).isoformat(), "limit": 50, "sort": "asc"}, timeout=20)
    out = []
    if r.ok:
        for n in r.json().get("news", []):
            t = datetime.fromisoformat(n["created_at"].replace("Z", "+00:00")).astimezone(ET)
            out.append((t.strftime("%m/%d %H:%M ET"), n["headline"], n.get("source", ""), n.get("url", "")))
    return out


_CHECKS = {}
WANT = set()   # symbols to collect in one pass of a day's decision log (set by the caller)


def _entry_checks(dp, sym):
    key = str(dp)
    if key not in _CHECKS:
        want = WANT | {sym}
        found = {s: [] for s in want}
        with open(dp) as fh:
            for line in fh:
                if '"kind": "entry"' not in line:
                    continue
                i = line.find('"symbol": "')
                s = line[i + 11:line.find('"', i + 11)] if i >= 0 else None
                if s in found:
                    found[s].append(json.loads(line))
        _CHECKS[key] = found
    if sym not in _CHECKS[key]:
        _CHECKS.pop(key)
        return _entry_checks(dp, sym)
    return _CHECKS[key][sym]


def trades_for(sym, day):
    p = TRADE / "data" / "trades" / f"{day}_trades.jsonl"
    out = []
    if p.exists():
        for l in open(p):
            t = json.loads(l)
            if t.get("symbol") == sym:
                out.append(t)
    return out


def et(iso):
    t = datetime.fromisoformat(iso)
    if t.tzinfo is None:            # decision logs are naive UTC
        return (t - timedelta(hours=4)).strftime("%H:%M")
    return t.astimezone(__import__("zoneinfo").ZoneInfo("America/New_York")).strftime("%H:%M")


def section(sym, day):
    R = load_minutes(sym, day)
    reg = [r for r in R if r["session"] == "reg"]
    f = lambda r, k: float(r[k])
    o = f(reg[0], "open")
    hi = max(reg, key=lambda r: f(r, "high"))
    lo = min(reg, key=lambda r: f(r, "low"))
    pre = [r for r in R if r["session"] == "pre"]
    tr = trades_for(sym, day)

    # chart
    x = [datetime.strptime(day + " " + r["time_et"], "%Y-%m-%d %H:%M") for r in R]
    fig, (a, b) = plt.subplots(2, 1, figsize=(11, 5.6), sharex=True, gridspec_kw={"height_ratios": [3, 1]})
    a.plot(x, [f(r, "close") for r in R], color="#1f3a5f", lw=1, label="price (1-min close)")
    a.plot([datetime.strptime(day + " " + r["time_et"], "%Y-%m-%d %H:%M") for r in reg],
           [f(r, "vwap_regular") for r in reg], color="#c0392b", lw=1, label="VWAP")
    if pre:
        a.axvspan(x[0], datetime.strptime(day + " 09:30", "%Y-%m-%d %H:%M"), color="#eeeeee", label="premarket")
    for t in tr:
        for k, px, col, lab in (("entry_time", t.get("entry_price"), "green", "BUY"),
                                ("exit_time", t.get("exit_price"), "red", "SELL")):
            if t.get(k) and px:
                tm = datetime.strptime(day + " " + et(t[k]), "%Y-%m-%d %H:%M")
                a.scatter([tm], [px], color=col, zorder=5, s=35)
                a.annotate(f"{lab} {px:.2f}", (tm, px), xytext=(4, 6), textcoords="offset points", fontsize=7, color=col)
    a.set_title(f"{sym} {day} — premarket to close (1-minute)", fontsize=11)
    a.legend(fontsize=7, loc="upper left")
    a.grid(alpha=.3)
    b.bar(x, [f(r, "volume") for r in R], width=0.0006,
          color=["#999" if r["session"] == "pre" else "#1f3a5f" for r in R])
    b.grid(alpha=.3)
    b.xaxis.set_major_formatter(md.DateFormatter("%H:%M"))
    plt.tight_layout()
    png = HERE / f"{sym}_{day}.png"
    plt.savefig(png, dpi=120)
    plt.close()

    L = [f"## {sym} — {day}", "", f"![]({png})", ""]
    L += ["| Open | Low (time) | High (time) | Close | Open→close | Premarket high / volume | Day volume |", "|---|---|---|---|---|---|---|",
          f"| {o:.2f} | {f(lo,'low'):.2f} ({lo['time_et']}) | {f(hi,'high'):.2f} ({hi['time_et']}) | {f(reg[-1],'close'):.2f} | "
          f"{(f(reg[-1],'close')/o-1)*100:+.1f}% | {max((f(r,'high') for r in pre), default=0):.2f} / "
          f"{int(sum(f(r,'volume') for r in pre)):,} | {int(sum(f(r,'volume') for r in reg)):,} |", ""]

    L += ["**Price vs the open and VWAP**", "", "| Time | Price | vs open | VWAP | vs VWAP |", "|---|---|---|---|---|"]
    marks = ["09:45", "10:00", "10:30", "11:00", "11:30", "12:00", "12:30", "13:00", "13:30", "14:00", "14:30", "15:00", "15:30", "15:59"]
    for m in marks:
        r = next((r for r in reg if r["time_et"] >= m), None)
        if r and r["vwap_regular"]:
            c, vw = f(r, "close"), f(r, "vwap_regular")
            L.append(f"| {r['time_et']} | {c:.2f} | {(c/o-1)*100:+.1f}% | {vw:.2f} | {(c/vw-1)*100:+.1f}% |")
    vol = Counter()
    for r in reg:
        vol[r["time_et"][:2]] += f(r, "volume")
    L += ["", "Volume by hour: " + " · ".join(f"{h}:00 {int(v/1000)}k" for h, v in sorted(vol.items())), ""]

    # scanner lists
    L += ["**Scanner lists that day**", ""]
    files = sorted(glob.glob(str(TRADE / "data" / "candidates" / f"{day}_scanner*.json")))
    rows = []
    for fp in files:
        lst = json.load(open(fp))
        syms = [c["symbol"] for c in lst]
        tag = fp.split("_scanner")[1].replace(".json", "").strip("_") or "0928"
        tag = "9:28" if tag == "0928" else f"{tag[:2]}:{tag[2:]}"
        if sym in syms:
            c = lst[syms.index(sym)]
            m = c["metrics"]
            rows.append(f"| {tag} | rank {syms.index(sym)+1} | {c['candidate_score']} | {m.get('gap_pct')}% | {m.get('rvol')} |")
        else:
            rows.append(f"| {tag} | not on the list | | | |")
    if rows:
        L += ["| Scan | AGEN | Score | Gap | RVOL |".replace("AGEN", sym), "|---|---|---|---|---|"] + rows
    else:
        L.append("No scanner lists saved for this day.")
    if day <= "2026-09-25":
        L += ["", "_Note: 9:28 scans before 9/25 10:05 used two-session-old prior-day data (bug fixed 9/25)._"]
    L.append("")

    # decisions
    dp = TRADE / "data" / "decisions" / f"{day}.jsonl"
    L += ["**Bot entry checks**", ""]
    if dp.exists():
        ch = _entry_checks(dp, sym)
        if ch:
            cnt = Counter()
            for d in ch:
                why = (d.get("reasons_against") or d.get("reasons_for") or [""])[0]
                cnt[(d["state"], re.sub(r"[\d.]+", "#", why)[:70])] += 1
            L += [f"{len(ch):,} checks, {et(ch[0]['timestamp'])}–{et(ch[-1]['timestamp'])} ET.", "",
                  "| Decision | Checks |", "|---|---|"]
            L += [f"| {s} — {w} | {n:,} |" for (s, w), n in cnt.most_common(7)]
        else:
            L.append("The bot never checked it for entry (not on its watchlist).")
        if day == "2026-09-22":
            L.append("_9/22: the bot crashed at 10:49 ET — no checks after that._")
    else:
        L.append("No decision log for this day.")
    L.append("")

    # news
    L += ["**News (3 days before through the close)**", ""]
    nw = news_for(sym, day)
    if nw:
        L += [f"- {t} — {h} ({src})" for t, h, src, url in nw]
    else:
        L.append("No news items found.")
    L.append("")

    # trades
    L += ["**Bot trades**", ""]
    if tr:
        for t in tr:
            L.append(f"- Bought {t.get('qty')} at ${t.get('entry_price'):.2f} ({et(t['entry_time'])}), "
                     + (f"sold at ${t.get('exit_price'):.2f} ({et(t['exit_time'])}) — {t.get('exit_reason','')[:80]} — "
                        f"**${t.get('pl_dollars', 0):+.2f}**" if t.get("exit_time") else "still open"))
            if t.get("exit_time"):
                held = (f(reg[-1], "close") - t["entry_price"]) * t["qty"]
                L.append(f"  - held to the close instead: about ${held:+.0f}")
    else:
        L.append("No trades.")
    L.append("")
    return "\n".join(L)


if __name__ == "__main__":
    # python3 day_report.py AGEN 2026-09-21 2026-09-22     (one symbol, several days)
    # python3 day_report.py --day 2026-09-28 INLF YDES ... (several symbols, one day)
    if sys.argv[1] == "--day":
        day, syms = sys.argv[2], sys.argv[3:]
        WANT.update(syms)
        for sym in syms:
            (HERE / f"{sym}_{day}.md").write_text(section(sym, day))
            print("done", sym, flush=True)
    else:
        sym = sys.argv[1]
        for day in sys.argv[2:]:
            (HERE / f"{sym}_{day}.md").write_text(section(sym, day))
            print("done", day)
