#!/usr/bin/env python3
"""
Cheap intraday check: compare live_prices.json against each watchlist entry's
trigger_price_cad/trigger_direction from the latest daily_snapshots/ scan.
No web searches, no Claude invocation -- just arithmetic against data
fetch_prices.py already refreshes every 5 minutes, run from the same hourly
cron as check_positions.py (see check_positions.sh).

This exists because a genuine intraday move on a watchlist name (e.g. a
resistance break) previously sat invisible until the next morning's Step 1c
re-check, even though fetch_prices.py was already polling that ticker's
price every 5 minutes all day. This script itself does NOT re-confirm the
setup (no volume/MACD/pattern check, no Claude invocation) -- it only
reports that the ONE numeric condition the last scan flagged as missing has
now been crossed.

Added 2026-08-28: check_positions.sh (the caller) now reads this script's
stdout and, for each newly-triggered ticker, launches a separate narrow
`claude -p` call running watchlist_trigger_reconfirm_prompt.md -- a
Questrade-only re-score (no web search) that can auto-execute the same way
run_daily_scan.sh's Step 6 does if the ticker genuinely still qualifies.
That real reconfirmation lives entirely in check_positions.sh + the reconfirm
prompt, not here -- this script stays pure arithmetic. Step 1c still fully
re-evaluates the ticker tomorrow regardless of what the reconfirm flow finds.

Only fires for watchlist entries where the last scan filled in
trigger_price_cad + trigger_direction (see daily_scan_prompt.md) -- a
`waiting_for` condition that isn't a single crossable price level (e.g.
"needs a volume figure") has no mechanical trigger and just waits for
tomorrow's scan, same as before this script existed.

Prints one line per newly-triggered ticker to stdout. De-dupes against
watchlist_alerts_state.json keyed by ticker + the snapshot date the trigger
came from, so a stale alert can't repeat all day and a new scan (which
supersedes the old trigger) always gets a fresh chance to fire.

Run manually:  python3 check_watchlist_triggers.py
Cron: folded into check_positions.sh's existing hourly run -- no separate
crontab entry.
"""
import json
import sys
from pathlib import Path

BASE = Path(__file__).parent
sys.path.insert(0, str(BASE))
from is_trading_day import is_trading_day

if not is_trading_day():
    sys.exit(0)

SNAPSHOTS_DIR = BASE / 'daily_snapshots'
prices_file = BASE / 'live_prices.json'
state_file = BASE / 'watchlist_alerts_state.json'


def load_json(path, default=None):
    if not path.exists():
        return default
    with open(path) as f:
        return json.load(f)


def latest_snapshot_path():
    files = sorted(SNAPSHOTS_DIR.glob('swing_trade_*.json'))
    return files[-1] if files else None


snap_path = latest_snapshot_path()
if snap_path is None:
    sys.exit(0)

snap_date = snap_path.stem.replace('swing_trade_', '')
snap = load_json(snap_path, {}) or {}
watchlist = (snap.get('signals', {}) or {}).get('watchlist', []) or []

prices = (load_json(prices_file, {}) or {}).get('prices', {})
state = load_json(state_file, {}) or {}

# Drop stale state for tickers no longer on the current watchlist.
current_tickers = {w['ticker'].upper() for w in watchlist if w.get('ticker')}
state = {k: v for k, v in state.items() if k.split('|', 1)[0] in current_tickers}

new_alerts = []

for entry in watchlist:
    ticker = entry.get('ticker', '').upper()
    trigger = entry.get('trigger_price_cad')
    direction = entry.get('trigger_direction')
    if not ticker or trigger is None or direction not in ('above', 'below'):
        continue

    key = f"{ticker}|{snap_date}"
    if state.get(key):
        continue

    quote = prices.get(ticker)
    if not quote or quote.get('price') is None:
        continue
    price = quote['price']

    crossed = (direction == 'above' and price >= trigger) or (direction == 'below' and price <= trigger)
    if not crossed:
        continue

    state[key] = True
    waiting_for = entry.get('waiting_for', '')
    new_alerts.append(
        f"{ticker} WATCHLIST TRIGGER: ${price:.2f} crossed {direction} ${trigger:.2f} "
        f"(not a re-confirmed setup) -- was waiting for: {waiting_for[:120]}"
    )

with open(state_file, 'w') as f:
    json.dump(state, f, indent=2)

for msg in new_alerts:
    print(msg)
