#!/usr/bin/env python3
"""Forward D+1/D+3 outcomes for first-LIVE Trend Break observations."""

import json
import os

from neobdm_common import datetime
from tradingview_outcomes import fetch_daily_bars, resolve_observation
from trend_break_history import HISTORY_DIR
from tv_enrichment import atomic_write_json


ROOT = os.path.dirname(os.path.abspath(__file__))
OUTCOMES_FILE = os.path.join(ROOT, "trend_break_outcomes.json")
SCHEMA_VERSION = "1.0"
REFRESH_INTERVAL_SECONDS = 60 * 60


def _load_json(path, fallback):
    try:
        with open(path, "r", encoding="utf-8") as handle:
            return json.load(handle)
    except (FileNotFoundError, json.JSONDecodeError, OSError):
        return fallback


def _dates(history_dir):
    index = _load_json(os.path.join(history_dir, "index.json"), {})
    return index.get("available_dates") or []


def first_live_observations(history_dir=HISTORY_DIR):
    observations = []
    for date in _dates(history_dir):
        daily = _load_json(os.path.join(history_dir, f"{date}.json"), {})
        first = {}
        for snapshot in daily.get("snapshots") or []:
            if snapshot.get("snapshot_phase") != "LIVE" or not snapshot.get("market_open"):
                continue
            timestamp = snapshot.get("timestamp")
            for signal in snapshot.get("signals") or []:
                ticker = str(signal.get("ticker") or "").upper()
                price = float(signal.get("price") or 0)
                if ticker and price > 0 and ticker not in first:
                    first[ticker] = {
                        "observation_id": f"TB:{date}:{ticker}:{timestamp}",
                        "date": date,
                        "ticker": ticker,
                        "detected_at": timestamp,
                        "entry_price": price,
                        "signal_class": signal.get("status"),
                    }
        observations.extend(first.values())
    return observations


def build_outcome_ledger(
    history_dir=HISTORY_DIR,
    bars_by_ticker=None,
    prior_records=None,
):
    observations = first_live_observations(history_dir)
    if bars_by_ticker is None:
        bars_by_ticker = fetch_daily_bars(
            observation["ticker"] for observation in observations
        )
    prior_by_key = {
        (str(record.get("date") or ""), str(record.get("ticker") or "").upper()): record
        for record in (prior_records or [])
    }
    rank = {"PENDING": 0, "D1_RESOLVED": 1, "D3_RESOLVED": 2}
    records = []
    for observation in observations:
        resolved = resolve_observation(
            observation,
            bars_by_ticker.get(observation["ticker"]) or [],
        )
        resolved["observation_id"] = observation["observation_id"]
        resolved["signal_class"] = observation.get("signal_class")
        prior = prior_by_key.get((observation["date"], observation["ticker"]))
        if (
            prior
            and rank.get(prior.get("status"), 0)
            > rank.get(resolved.get("status"), 0)
        ):
            resolved = prior
        records.append(resolved)
    records.sort(key=lambda row: (row["date"], row["ticker"]))
    d1 = sum(record.get("label_d1") in (0, 1) for record in records)
    d3 = sum(record.get("label_d3") in (0, 1) for record in records)
    return {
        "schema_version": SCHEMA_VERSION,
        "generated_at": datetime.now().isoformat(timespec="seconds"),
        "source": "TradingView daily bars with exchange timestamps",
        "entry_definition": "harga snapshot LIVE pertama per ticker-date",
        "cost_assumption_pct": 0.30,
        "summary": {
            "observations": len(records),
            "d1_labeled": d1,
            "d3_labeled": d3,
            "pending_d1": len(records) - d1,
        },
        "records": records,
    }


def refresh_outcomes(
    history_dir=HISTORY_DIR,
    output_path=OUTCOMES_FILE,
    force=False,
):
    existing = _load_json(output_path, {})
    generated_at = existing.get("generated_at")
    current_count = len(first_live_observations(history_dir))
    existing_count = int((existing.get("summary") or {}).get("observations") or 0)
    if not force and generated_at and current_count == existing_count:
        try:
            age = (datetime.now() - datetime.fromisoformat(generated_at)).total_seconds()
        except (TypeError, ValueError):
            age = REFRESH_INTERVAL_SECONDS
        if age < REFRESH_INTERVAL_SECONDS:
            return existing, False
    payload = build_outcome_ledger(
        history_dir=history_dir,
        prior_records=existing.get("records") or [],
    )
    atomic_write_json(output_path, payload, indent=2)
    return payload, True


if __name__ == "__main__":
    payload, refreshed = refresh_outcomes(force=True)
    print(json.dumps({
        "refreshed": refreshed,
        "generated_at": payload.get("generated_at"),
        "summary": payload.get("summary"),
    }, indent=2))
