"""
morning_prefilter.py — pre-market scan using IBKR market data.
Connects to TWS, scans for gapping stocks in the $1–$20 range, writes watchlist.txt.
"""
import argparse
import json
import os
import sys
import time
from datetime import datetime
from pathlib import Path
from zoneinfo import ZoneInfo

from dotenv import load_dotenv

load_dotenv(Path(".env"))
sys.path.insert(0, str(Path(__file__).parent))

from src.ibkr_client import IBKRClient
from src.notify import notify

WATCHLIST_PATH = Path("watchlist.txt")
DEFAULT_MIN_GAP_PCT = 3.0
DEFAULT_MIN_PRICE   = 1.0
DEFAULT_MAX_PRICE   = 20.0
MAX_SURVIVORS       = 20
ET = ZoneInfo("America/New_York")

parser = argparse.ArgumentParser()
parser.add_argument("--min-gap",   type=float, default=DEFAULT_MIN_GAP_PCT)
parser.add_argument("--min-price", type=float, default=DEFAULT_MIN_PRICE)
parser.add_argument("--max-price", type=float, default=DEFAULT_MAX_PRICE)
parser.add_argument("--dry-run",   action="store_true")
args = parser.parse_args()

start = time.time()

host      = os.getenv("IBKR_HOST", "127.0.0.1")
port      = int(os.getenv("IBKR_PORT", 7497))
client_id = int(os.getenv("IBKR_CLIENT_ID", 2))

print("Connecting to IBKR...", file=sys.stderr)
try:
    ibkr = IBKRClient(host, port, client_id)
except Exception as e:
    msg = f"IBKR connection failed: {e}"
    print(msg, file=sys.stderr)
    try:
        notify("Prefilter FAILED", msg, "high")
    except Exception:
        pass
    print(json.dumps({"success": False, "error": msg}))
    sys.exit(1)

print(f"Scanning universe: price ${args.min_price}–${args.max_price}...", file=sys.stderr)
try:
    symbols = ibkr.scan_universe(
        min_price=args.min_price,
        max_price=args.max_price,
    )
except Exception as e:
    symbols = []
    print(f"[WARN] Scanner failed: {e}", file=sys.stderr)

total = len(symbols)
print(f"Universe: {total} symbols. Fetching daily bars...", file=sys.stderr)

if total == 0:
    msg = "IBKR scanner returned 0 symbols"
    try:
        notify("Prefilter FAILED", msg, "high")
    except Exception:
        pass
    ibkr.disconnect()
    print(json.dumps({"success": False, "error": msg}))
    sys.exit(1)

survivors  = []
below_gap  = 0
below_price = 0
failed     = 0

for sym in symbols:
    try:
        df = ibkr.get_daily_bars(sym, days=3)
        if df is None or len(df) < 2:
            failed += 1
            continue

        prev_close = float(df["Close"].iloc[-2])
        today_open = float(df["Open"].iloc[-1])
        today_close = float(df["Close"].iloc[-1])
        today_high  = float(df["High"].iloc[-1])
        today_low   = float(df["Low"].iloc[-1])

        if prev_close == 0:
            failed += 1
            continue

        gap_pct = (today_close - prev_close) / prev_close * 100

        import math as _math
        if _math.isnan(gap_pct) or _math.isnan(today_close):
            failed += 1
            continue
        if today_close < args.min_price or today_close > args.max_price:
            below_price += 1
            continue
        if gap_pct < args.min_gap:
            below_gap += 1
            continue

        survivors.append({
            "ibkr":           sym,
            "gap_pct":        gap_pct,
            "today_open":     today_open,
            "today_close":    today_close,
            "yesterday_close": prev_close,
            "today_high":     today_high,
            "today_low":      today_low,
        })

    except Exception:
        failed += 1
        continue

ibkr.disconnect()

survivors.sort(key=lambda x: x["gap_pct"], reverse=True)
survivors = survivors[:MAX_SURVIVORS]

elapsed  = round(time.time() - start, 2)
now_et   = datetime.now(ET)
zone_name = now_et.strftime("%Z")

if not args.dry_run and survivors:
    with open(WATCHLIST_PATH, "w") as f:
        f.write(f"# Auto-generated by morning_prefilter.py at {now_et.strftime('%Y-%m-%d %H:%M')} {zone_name}\n")
        f.write(f"# Filters: gap >= {args.min_gap}%, price ${args.min_price}–${args.max_price}\n")
        f.write(f"# Source: IBKR market scanner\n")
        f.write(f"# Survivors: {len(survivors)} (capped at {MAX_SURVIVORS}) of {total}\n")
        f.write("#\n")
        f.write(f"# {'ticker':<8}  # gap +X.XX%  open $X.XX  prev $X.XX\n")
        for s in survivors:
            f.write(
                f"{s['ibkr']:<6}  "
                f"# gap +{s['gap_pct']:.2f}%  "
                f"open ${s['today_open']:.2f}  "
                f"prev ${s['yesterday_close']:.2f}\n"
            )

top_20 = [f"{s['ibkr']} (+{s['gap_pct']:.2f}%)" for s in survivors]

summary = {
    "success":          True,
    "total_screened":   total,
    "survivors_count":  len(survivors),
    "below_gap":        below_gap,
    "below_price":      below_price,
    "failed":           failed,
    "elapsed_seconds":  elapsed,
    "top_20_survivors": top_20,
    "watchlist_path":   str(WATCHLIST_PATH) if not args.dry_run else "dry-run (not written)",
}

if not args.dry_run and survivors:
    hm = now_et.strftime("%H:%M")
    survivor_list = "\n".join([f"• {s}" for s in top_20[:5]])
    body = f"{len(survivors)}/{total} survivors in {elapsed}s\n\n{survivor_list}"
    try:
        notify(f"Prefilter {hm} ET", body, "default")
    except Exception:
        pass

print(json.dumps(summary, indent=2))
