#!/usr/bin/env python3
"""
backtest_harness — runs a strategy backtest as a subprocess.

Reads a JSON config from stdin:
{
    "strategy_name": str,
    "feeds": [{"symbol": str, "period_seconds": int}, ...],
    "from_time": ...,
    "to_time": ...,
    "initial_capital": float,
    "paper": bool
}

Outputs JSON to stdout on success:
{
    "strategy_name": str,
    "feeds": [...],
    "initial_capital": float,
    "paper": bool,
    "total_candles": int,
    ... (metrics from run_backtest)
}

On error:
{"error": str}
"""

import asyncio
import json
import os
import sys
import traceback
from pathlib import Path

# Ensure dexorder package is importable when run as a subprocess
sys.path.insert(0, str(Path(__file__).resolve().parent.parent.parent))

_OHLC_EXTRA_COLUMNS = [
    "volume", "buy_vol", "sell_vol",
    "open_time", "high_time", "low_time", "close_time",
    "open_interest",
]


async def _run(cfg: dict) -> dict:
    strategy_name = cfg["strategy_name"]
    feeds = cfg["feeds"]
    from_time = cfg.get("from_time")
    to_time = cfg.get("to_time")
    initial_capital = float(cfg.get("initial_capital", 10_000.0))
    paper = bool(cfg.get("paper", True))

    # -------------------------------------------------------------------------
    # Initialize API
    # -------------------------------------------------------------------------
    try:
        import yaml

        config_path = os.environ.get("CONFIG_PATH", "/app/config/config.yaml")
        secrets_path = os.environ.get("SECRETS_PATH", "/app/config/secrets.yaml")

        config_data = {}
        secrets_data = {}
        if Path(config_path).exists():
            with open(config_path) as f:
                config_data = yaml.safe_load(f) or {}
        if Path(secrets_path).exists():
            with open(secrets_path) as f:
                secrets_data = yaml.safe_load(f) or {}

        data_cfg = config_data.get("data", {})
        iceberg_cfg = data_cfg.get("iceberg", {})
        relay_cfg = data_cfg.get("relay", {})

        from dexorder.api import set_api, API
        from dexorder.impl.charting_api_impl import ChartingAPIImpl
        from dexorder.impl.data_api_impl import DataAPIImpl

        data_api = DataAPIImpl(
            iceberg_catalog_uri=iceberg_cfg.get("catalog_uri", "http://iceberg-catalog:8181"),
            relay_endpoint=relay_cfg.get("endpoint", "tcp://relay:5559"),
            notification_endpoint=relay_cfg.get("notification_endpoint", "tcp://relay:5558"),
            namespace=iceberg_cfg.get("namespace", "trading"),
            s3_endpoint=iceberg_cfg.get("s3_endpoint") or secrets_data.get("s3_endpoint"),
            s3_access_key=iceberg_cfg.get("s3_access_key") or secrets_data.get("s3_access_key"),
            s3_secret_key=iceberg_cfg.get("s3_secret_key") or secrets_data.get("s3_secret_key"),
            s3_region=iceberg_cfg.get("s3_region") or secrets_data.get("s3_region"),
            request_timeout=240.0,
        )
        set_api(API(charting=ChartingAPIImpl(), data=data_api))
    except Exception as e:
        return {"error": f"API initialization failed: {e}"}

    # -------------------------------------------------------------------------
    # Locate strategy
    # -------------------------------------------------------------------------
    data_dir = Path(os.environ.get("DATA_DIR", "/app/data"))
    try:
        from dexorder.tools.python_tools import get_category_manager, sanitize_name
        category_manager = get_category_manager(data_dir)
        safe_name = sanitize_name(strategy_name)
        impl_path = category_manager.src_dir / "strategy" / safe_name / "implementation.py"
        if not impl_path.exists():
            return {"error": f"Strategy '{strategy_name}' not found (looked at {impl_path})"}
    except Exception as exc:
        return {"error": f"Failed to locate strategy: {exc}"}

    # -------------------------------------------------------------------------
    # Register custom indicators and load strategy class
    # -------------------------------------------------------------------------
    try:
        from dexorder.nautilus.backtest_runner import _setup_custom_indicators
        _setup_custom_indicators(category_manager.src_dir)
    except Exception as exc:
        sys.stderr.write(f"WARNING: custom indicator setup failed: {exc}\n")

    try:
        from dexorder.nautilus.backtest_runner import _load_strategy_class
        strategy_class = _load_strategy_class(impl_path)
    except Exception:
        return {"error": f"Strategy load failed:\n{traceback.format_exc()}"}

    # -------------------------------------------------------------------------
    # Fetch OHLC data
    # -------------------------------------------------------------------------
    from dexorder.api import get_api
    from dexorder.nautilus.pandas_strategy import make_feed_key

    api = get_api()
    parsed_feeds = [(f["symbol"], int(f["period_seconds"])) for f in feeds]
    ohlc_dfs = {}
    total_candles = 0

    for ticker, period_seconds in parsed_feeds:
        feed_key = make_feed_key(ticker, period_seconds)
        try:
            df = await api.data.historical_ohlc(
                ticker=ticker,
                period_seconds=period_seconds,
                start_time=from_time,
                end_time=to_time,
                extra_columns=_OHLC_EXTRA_COLUMNS,
            )
        except Exception as exc:
            return {"error": f"OHLC fetch failed for {feed_key}: {exc}"}

        if df.empty:
            return {"error": f"No OHLC data for {feed_key} in the requested range"}

        ohlc_dfs[feed_key] = df
        total_candles += len(df)

    # -------------------------------------------------------------------------
    # Run backtest (synchronous)
    # -------------------------------------------------------------------------
    try:
        from dexorder.nautilus.backtest_runner import run_backtest
        metrics = run_backtest(
            strategy_class=strategy_class,
            feeds=parsed_feeds,
            ohlc_dfs=ohlc_dfs,
            initial_capital=initial_capital,
            paper=paper,
        )
    except Exception:
        return {"error": f"Backtest failed:\n{traceback.format_exc()}"}

    return {
        "strategy_name": strategy_name,
        "feeds": [{"symbol": t, "period_seconds": p} for t, p in parsed_feeds],
        "initial_capital": initial_capital,
        "paper": paper,
        "total_candles": total_candles,
        **metrics,
    }


def main():
    cfg = json.loads(sys.stdin.read())
    result = asyncio.run(_run(cfg))
    print(json.dumps(result))


if __name__ == "__main__":
    main()
