from __future__ import annotations

import argparse
import json
import signal
import urllib.request
from datetime import datetime, timezone
from pathlib import Path
from threading import Event, Thread

from hanz_realtime.gateway import RealtimeGateway
from hanz_realtime.yahoo_provider import YahooPollingProvider
from hanz_radar.engine import OpportunityRadar
from hanz_radar.bridge import RadarDecisionBridge
from hanz_server.service import HanzRealtimeService
from hanz_server.store import SQLiteStateStore


NTFY_URL = "https://ntfy.sh/Hanz-News"


def send_ntfy(signal_data):
    symbol = signal_data.symbol
    status = signal_data.status
    evidence = signal_data.evidence_score
    anomaly = signal_data.anomaly_score
    reason = signal_data.reason

    if status == "READY":
        priority = "urgent"
        title = f"HANZ READY - {symbol}"
    else:
        priority = "default"
        title = f"HANZ PREPARE - {symbol}"

    message = (
        f"{symbol}\n"
        f"Status: {status}\n"
        f"Evidence: {evidence}\n"
        f"Anomaly: {anomaly:.1f}\n"
        f"Reason: {reason}"
    )

    req = urllib.request.Request(
        NTFY_URL,
        data=message.encode("utf-8"),
        method="POST",
        headers={
            "Title": title,
            "Priority": priority,
            "Tags": "chart_with_upwards_trend",
        },
    )

    try:
        urllib.request.urlopen(req, timeout=10).read()

        print(
            json.dumps(
                {
                    "event": "ntfy-sent",
                    "symbol": symbol,
                    "status": status,
                }
            ),
            flush=True,
        )

    except Exception as exc:
        print(
            json.dumps(
                {
                    "event": "ntfy-error",
                    "symbol": symbol,
                    "error": str(exc),
                }
            ),
            flush=True,
        )


def main() -> int:
    parser = argparse.ArgumentParser(
        description="HANZ Yahoo IDX Opportunity Radar"
    )

    parser.add_argument(
        "--symbols",
        default="BBCA.JK,BMRI.JK,BBRI.JK",
    )

    parser.add_argument(
        "--poll-seconds",
        type=int,
        default=60,
    )

    parser.add_argument(
        "--rank-seconds",
        type=int,
        default=60,
    )

    parser.add_argument(
        "--db",
        default="/home/opc/hanz-radar/runtime/hanz.sqlite3",
    )

    parser.add_argument(
        "--snapshot",
        default="/home/opc/hanz-radar/runtime/radar_snapshot.json",
    )

    args = parser.parse_args()

    symbols = [
        item.strip()
        for item in args.symbols.split(",")
        if item.strip()
    ]

    provider = YahooPollingProvider(
        symbols=symbols,
        market="BEI",
        poll_seconds=args.poll_seconds,
    )

    gateway = RealtimeGateway()
    radar = OpportunityRadar(max_results=25)
    bridge = RadarDecisionBridge(radar)

    service = HanzRealtimeService(
        store=SQLiteStateStore(args.db),
    )

    snapshot_path = Path(args.snapshot)
    stop = Event()

    # Menyimpan status terakhir yang SUDAH dikirim ke HP.
    # PREPARE tidak akan dikirim berulang.
    # Jika naik PREPARE -> READY, alert baru langsung dikirim.
    notified_status = {}

    def shutdown(*_):
        stop.set()

        print(
            json.dumps(
                {"event": "hanz-yahoo-stop"}
            ),
            flush=True,
        )

    signal.signal(signal.SIGINT, shutdown)
    signal.signal(signal.SIGTERM, shutdown)

    def rank_loop():
        while not stop.wait(args.rank_seconds):
            try:
                now = datetime.now(timezone.utc)
                states = list(gateway.states.values())

                snapshot = radar.evaluate(
                    states,
                    now=now,
                )

                snapshot_path.parent.mkdir(
                    parents=True,
                    exist_ok=True,
                )

                               # Build TOP 10 watchlist from all current states
                # without changing RADAR thresholds.
                radar_symbols = {
                    item.symbol
                    for item in snapshot.active_signals
                }

                watchlist = []

                for state in states:
                    try:
                        if state.symbol in radar_symbols:
                            continue

                        evidence = radar._evidence_score(state)

                        watchlist.append(
                            {
                                "symbol": state.symbol,
                                "market": getattr(state, "market", "BEI"),
                                "price": getattr(state, "last_price", None),
                                "evidence": evidence,
                                "anomaly": getattr(
                                    state,
                                    "anomaly_score",
                                    0.0,
                                ),
                                "velocity": getattr(
                                    state,
                                    "velocity",
                                    0.0,
                                ),
                                "price_change_pct": getattr(
                                    state,
                                    "price_change_pct",
                                    0.0,
                                ),
                                "reason": radar._reason(state),
                                "risk": radar._risk(state),
                            }
                        )

                    except Exception:
                        continue

                watchlist.sort(
                    key=lambda x: (
                        x["evidence"],
                        x["anomaly"],
                    ),
                    reverse=True,
                )

                watchlist = watchlist[:10]

                snapshot_data = snapshot.to_dict()
                snapshot_data["WATCHLIST"] = watchlist
                snapshot_path.write_text(
                    json.dumps(
                        snapshot_data,
                        ensure_ascii=False,
                        indent=2,
                    ),
                    encoding="utf-8",
                )

                print(
                    json.dumps(
                        {
                            "event": "radar-heartbeat",
                            "total": snapshot.total_symbols,
                            "candidates": len(
                                snapshot.active_signals
                            ),
                            "updated_at": now.isoformat(),
                        }
                    ),
                    flush=True,
                )

                active_symbols = set()

                for item in snapshot.active_signals:
                    symbol = item.symbol
                    status = item.status

                    active_symbols.add(symbol)

                    # EARLY hanya dipantau diam-diam.
                    if status not in ("PREPARE", "READY"):
                        continue

                    previous = notified_status.get(symbol)

                    # Kirim jika belum pernah dikirim,
                    # atau status meningkat PREPARE -> READY.
                    should_notify = (
                        previous is None
                        or (
                            previous == "PREPARE"
                            and status == "READY"
                        )
                    )

                    if should_notify:
                        send_ntfy(item)
                        notified_status[symbol] = status

                # Kalau saham keluar dari radar,
                # reset supaya opportunity baru nanti
                # bisa menghasilkan notification lagi.
                for symbol in list(notified_status):
                    if symbol not in active_symbols:
                        del notified_status[symbol]

                for decision in bridge.decisions(
                    states,
                    now=now,
                ):
                    service.process_decision(
                        decision
                    )

            except Exception as exc:
                print(
                    json.dumps(
                        {
                            "event": "radar-error",
                            "error": str(exc),
                        }
                    ),
                    flush=True,
                )

    print(
        json.dumps(
            {
                "event": "hanz-yahoo-start",
                "symbols": symbols,
                "poll_seconds": args.poll_seconds,
                "rank_seconds": args.rank_seconds,
                "ntfy": "Hanz-News",
            }
        ),
        flush=True,
    )

    worker = Thread(
        target=rank_loop,
        daemon=True,
    )

    worker.start()

    try:
        for tick in provider.stream():
            if stop.is_set():
                break

            state = gateway.ingest(tick)

            print(
                json.dumps(
                    {
                        "event": "tick",
                        "symbol": tick.symbol,
                        "price": tick.price,
                        "volume": tick.volume,
                        "score": state.anomaly_score,
                        "timestamp": tick.timestamp.isoformat(),
                    }
                ),
                flush=True,
            )

    finally:
        stop.set()
        worker.join(timeout=2)

    return 0


if __name__ == "__main__":
    raise SystemExit(main())
