"""IBKR live adapter for intraday MA-slope confirm_entry_4b (platform orchestrator)."""

from __future__ import annotations

import json
from pathlib import Path

from RenTech.live.ma_slope_intraday_live import (
    build_entry_intents,
    build_exit_intents,
    compute_target_weights,
    default_universe,
    in_entry_window,
    in_exit_window,
    load_day_state,
    load_intraday_panels,
    load_sector_map,
    recommendation_payload,
    save_day_state,
)
from RenTech.live.ibkr_equity_orders import last_price, place_stock_order
from RenTech.live.protocols import RiskAction, StrategyContext, StrategyCycleReport


class MaSlopeIntradayIbkrStrategy:
    strategy_id = "ma_slope_intraday"

    async def audit(self, ctx: StrategyContext) -> StrategyCycleReport:
        return StrategyCycleReport(strategy_id=self.strategy_id, ok=True, messages=["audit: no-op"])

    async def run_cycle(self, ctx: StrategyContext) -> StrategyCycleReport:
        report = StrategyCycleReport(strategy_id=self.strategy_id, ok=True)
        scfg = ctx.strategy_config
        state_path = Path(str(scfg.get("state_path", "RenTech/data/live_state/ma_slope_intraday.json")))
        if not state_path.is_absolute():
            state_path = (ctx.repo_root / state_path).resolve()
        rec_out = Path(
            str(scfg.get("recommend_out", "RenTech/data/logs/ma_slope_intraday_ibkr_recommendation.json"))
        )
        if not rec_out.is_absolute():
            rec_out = (ctx.repo_root / rec_out).resolve()

        top_n = int(scfg.get("top_n", 10))
        max_tickers = int(scfg.get("max_tickers", 500))
        data_source = str(scfg.get("data_source", "alpaca_parquet"))
        entry_after = str(scfg.get("entry_after_et", "10:40"))
        entry_before = str(scfg.get("entry_before_et", "11:05"))
        exit_after = str(scfg.get("exit_after_et", "15:55"))
        exit_before = str(scfg.get("exit_before_et", "16:00"))

        day = load_day_state(state_path, ctx.today)
        rec_only = ctx.recommend_only

        if ctx.platform_risk.action == RiskAction.HALT_ALL:
            report.messages.append("platform halt")
            return report

        # Exit first
        if in_exit_window(ctx.now_et, exit_after=exit_after, exit_before=exit_before) and not day.get(
            "exit_done"
        ):
            intents = build_exit_intents(list(day.get("symbols") or []), ctx.portfolio.positions)
            payload = recommendation_payload(
                phase="exit_moc",
                targets={},
                meta={"symbols": day.get("symbols") or []},
                intents=intents,
                capital_budget_usd=ctx.capital_budget_usd,
            )
            rec_out.parent.mkdir(parents=True, exist_ok=True)
            rec_out.write_text(json.dumps(payload, indent=2) + "\n", encoding="utf-8")
            if not rec_only:
                for intent in intents:
                    await place_stock_order(ctx.ib, intent, recommend_only=False)
                    report.exits_attempted += 1
                save_day_state(state_path, ctx.today, {**day, "exit_done": True})
            report.messages.append(f"exit_moc n={len(intents)}")
            return report

        if not ctx.strategy_risk.allow_new_entries:
            report.messages.append("strategy risk blocks entries")
            return report
        if day.get("entry_done"):
            report.messages.append("entry already done today")
            return report
        if not (ctx.force_entry_now or in_entry_window(ctx.now_et, entry_after=entry_after, entry_before=entry_before)):
            report.messages.append("outside entry window")
            return report

        symbols = default_universe(max_tickers=max_tickers)
        intra = await load_intraday_panels(
            symbols, data_source=data_source, ib=ctx.ib, warmup_sessions=15  # type: ignore[arg-type]
        )
        targets, meta = compute_target_weights(
            intra, load_sector_map(), top_n=top_n, as_of_et=ctx.now_et
        )
        report.metadata = {"signal_meta": meta, "targets": targets}
        if meta.get("status") == "waiting_confirm" or not targets:
            report.messages.append(str(meta.get("status", "no targets")))
            return report

        prices = {}
        for sym in targets:
            try:
                prices[sym] = await last_price(ctx.ib, sym)
            except Exception as exc:
                report.messages.append(f"price {sym}: {exc}")

        intents = build_entry_intents(
            targets,
            ctx.portfolio.positions,
            capital_budget_usd=ctx.capital_budget_usd,
            prices=prices,
        )
        payload = recommendation_payload(
            phase="entry",
            targets=targets,
            meta=meta,
            intents=intents,
            capital_budget_usd=ctx.capital_budget_usd,
        )
        rec_out.write_text(json.dumps(payload, indent=2) + "\n", encoding="utf-8")

        if not rec_only:
            for intent in intents:
                await place_stock_order(ctx.ib, intent, recommend_only=False)
                report.entries_attempted += 1
            save_day_state(
                state_path,
                ctx.today,
                {
                    "entry_done": True,
                    "exit_done": False,
                    "symbols": sorted(targets),
                    "weights": targets,
                    "entry_run_id": ctx.run_id,
                },
            )
        report.messages.append(f"entry n={len(intents)}")
        return report
