"""
CV6 — Complete trade lifecycle capture.
Drives ONE trade through the REAL pipeline components (technicals, risk gate,
sizing, order manager + fake broker, fill tracker, broker-native SL, Trading
State Manager, capital/PnL, trade journal, dashboard) and records every stage's
actual output to flow_capture.json for the Excel export.

Market is closed (offline), so: the SCAN + AI stages use representative inputs
(clearly labelled), the broker is a fake that mimics Angel One responses, and
the market-hours risk layer is bypassed for the demo (it enforces live).
"""
import json
import logging
from datetime import datetime, timedelta

logging.getLogger("sqlalchemy").setLevel(logging.WARNING)
import main
from app.database.session import engine as _e
_e.echo = False
from loguru import logger as _lg
_lg.remove()

from app.database.session import SessionLocal
from app.state.state_models import PositionRecord, CapitalState, OrderRecord
from app.state.trading_state_manager import trading_state_manager as TSM
from app.state.trading_events import TradingEvent, EventType
from app.execution.execution_models import ExecutionRequest, OrderSide, OrderType, ProductType
from app.execution.order_manager import OrderManager
from app.dashboard.dashboard_service import dashboard_service
from app.autonomous.autonomous_engine import autonomous_engine
from app.autonomous.broker_allocator import BrokerAllocator
from app.risk_guard.risk_guard import risk_guard, RiskVerdict
from app.market.technicals import compute_technicals

STAGES = []
def stage(n, name, owner, mode, detail):
    STAGES.append({"step": n, "stage": name, "owner_file": owner,
                   "event": mode, "detail": detail})

# clean slate
db = SessionLocal()
for t in (PositionRecord, CapitalState, OrderRecord):
    db.query(t).delete()
db.commit(); db.close()
TSM.bootstrap(default_total_capital=500000)
TSM._total_capital=500000; TSM._available_capital=500000; TSM._deployed_capital=0
TSM._realized_pnl=0; TSM._positions.clear(); TSM._orders.clear()
autonomous_engine.config.mode = "REAL"

SYMBOL, EXCH = "RELIANCE", "NSE"
ENTRY = 2800.0

# ── 1. SCAN (representative — needs live market) ─────────────────────────────
stage(1, "Market Scan", "app/autonomous/smart_scanner.py", "SELECT",
      f"{SYMBOL} shortlisted from NSE universe | ltp=Rs.{ENTRY} change=+1.8% (representative: live scan needs market hours)")

# ── 2. REAL TECHNICALS (real math on real-shaped candles) ────────────────────
class FakeAngel:
    _connected = True
    def _get_symbol_token(self, s, e): return (s, "2885")
    def historical_data(self, params):
        rows=[]; now=datetime.now()
        for i in range(60,0,-1):
            p = ENTRY - 60 + (60-i)   # gently rising series
            ts=(now - timedelta(minutes=5*i)).strftime("%Y-%m-%d %H:%M:%S")
            rows.append([ts, p, p+3, p-2, p+1, 120000+i*10])
        return {"status": True, "data": rows}
    def positions(self): return {"data": []}
    def orders(self):
        return {"data":[{"orderid":"AO-ENTRY-1","status":"complete","filledshares":QTY,
            "averageprice":ENTRY,"tradingsymbol":SYMBOL,"transactiontype":"BUY","quantity":QTY}]}
    def place_order(self, od):
        FB_PLACED.append(od)
        return {"success": True, "order_id": "AO-SL-1" if od.get("ordertype")=="SL-M" else "AO-ENTRY-1"}
    def cancel_order(self, oid):
        FB_CANCELLED.append(oid); return {"success": True, "message": "cancelled"}

FB_PLACED, FB_CANCELLED = [], []
fake = FakeAngel()
tech = compute_technicals(SYMBOL, EXCH, broker=fake)
stage(2, "Real Technicals", "app/market/technicals.py", "COMPUTE",
      f"data_quality={tech.data_quality} | RSI={tech.rsi} EMA9={tech.ema_9} EMA21={tech.ema_21} "
      f"VWAP={tech.vwap} ATR={tech.atr} Supertrend={tech.supertrend} volR={tech.volume_ratio} "
      f"(computed from {tech.candles_used} real candles)")

# ── 3. AI DECISION (representative — needs AI keys + network) ─────────────────
AI_SIGNAL, AI_CONF = "BUY", 78.0
stage(3, "AI Consensus", "app/ai/ai_consensus.py", "DECIDE",
      f"signal={AI_SIGNAL} confidence={AI_CONF}% (representative: real consensus needs provider keys). "
      f"Gate: REAL trade only proceeds because technicals data_quality={tech.data_quality}")

# ── 4. SIZING + SL/TP ────────────────────────────────────────────────────────
alloc = BrokerAllocator(autonomous_engine.config.broker_allocation)
SL, TP = alloc.calculate_targets(ENTRY, "BUY", "INTRADAY")
QTY, VAL = alloc.calculate_position_size(TSM.snapshot()["capital"]["available"], ENTRY,
                                         autonomous_engine.config.risk_pct, SL, "INTRADAY")
stage(4, "Position Sizing", "app/autonomous/broker_allocator.py", "SIZE",
      f"qty={QTY} value=Rs.{VAL:.0f} | SL=Rs.{SL} TP=Rs.{TP} (risk %/capital based)")

# ── 5. RISK GATE ─────────────────────────────────────────────────────────────
_saved = risk_guard.check_entry
risk_guard.check_entry = lambda **kw: RiskVerdict.ok()   # market-hours bypass for offline demo
verdict = "ALLOWED (kill-switch/loss/drawdown clear; market-hours gate bypassed for offline demo)"
stage(5, "Risk Guard", "app/risk_guard/risk_guard.py", "GATE", verdict)

# ── 6-8. ORDER -> BROKER -> FILL -> BROKER SL ────────────────────────────────
om = OrderManager(); om.broker_manager.get_broker = lambda name=None: fake
req = ExecutionRequest(broker="angelone", symbol=SYMBOL, exchange=EXCH, side=OrderSide.BUY,
    quantity=QTY, order_type=OrderType.MARKET, product=ProductType.INTRADAY,
    stop_loss=SL, target_price=TP, idempotency_key="flow-demo-1")
resp = om.place_order(req)
stage(6, "Order Placed", "app/execution/order_manager.py", "ORDER",
      f"MARKET BUY {QTY} {SYMBOL} -> broker order_id={resp.order_id} (idempotency-guarded)")
stage(7, "Fill Confirmed", "app/execution/fill_tracker.py", "FILL",
      f"fill_status={resp.fill_status} filled_qty={resp.filled_quantity} avg=Rs.{resp.avg_fill_price} "
      f"(polled broker order book; 'accepted' != 'filled')")
stage(8, "Broker-native SL", "app/broker/angel_one.py", "PROTECT",
      f"STOPLOSS_MARKET placed at broker: sl_order_id={resp.sl_order_id} trigger=Rs.{SL} "
      f"sl_protected={resp.sl_protected} (survives a process crash)")

# order_manager already emitted the TSM fill event on the manual path
pos = TSM.get_position(SYMBOL)
cap = TSM.snapshot()["capital"]
stage(9, "Position Opened (TSM)", "app/state/trading_state_manager.py", "STATE",
      f"OPEN {pos['side']} {pos['qty']} {SYMBOL} @ Rs.{pos['entry_price']} SL={pos['stop_loss']} "
      f"sl_order_id={pos['sl_order_id']} | ONE source of truth, persisted to DB")
stage(10, "Capital Updated", "app/state/trading_state_manager.py", "CAPITAL",
      f"deployed=Rs.{cap['deployed']:.0f} available=Rs.{cap['available']:.0f} total=Rs.{cap['total']:.0f}")

# ── 11. PRICE TICK -> unrealized PnL ─────────────────────────────────────────
LTP = TP  # price moves up to target
TSM.apply_event(TradingEvent(event_type=EventType.PRICE_TICK, symbol=SYMBOL, ltp=LTP))
snap = TSM.snapshot()
stage(11, "Live Price / Unrealized", "app/autonomous/position_monitor.py", "TICK",
      f"ltp=Rs.{LTP} -> unrealized PnL=Rs.{snap['pnl']['unrealized']:.0f}")

# ── 12. TARGET HIT -> OCO close (cancel SL) + realized PnL + journal ─────────
# simulate the monitor's close path: cancel broker SL, then book close in TSM
fake.cancel_order(resp.sl_order_id)  # OCO: cancel the outstanding SL before exit
close_pnl = (LTP - ENTRY) * QTY
TSM.apply_event(TradingEvent(event_type=EventType.POSITION_CLOSED, symbol=SYMBOL, broker="angelone",
    exchange=EXCH, side="BUY", filled_quantity=QTY, avg_price=LTP, pnl=close_pnl,
    reason="TARGET_HIT", style="INTRADAY", mode="REAL", source="flow_demo"))
snap = TSM.snapshot()
stage(12, "Target Hit / OCO Close", "app/autonomous/autonomous_engine.py", "CLOSE",
      f"SL {resp.sl_order_id} cancelled (OCO, no double-sell) -> exit @ Rs.{LTP} "
      f"realized PnL=Rs.{close_pnl:.0f}")
stage(13, "Trade Journal", "app/history/history_service.py", "JOURNAL",
      f"one EXECUTED row written to trade_history (symbol {SYMBOL}, pnl Rs.{close_pnl:.0f})")
stage(14, "Capital / PnL Final", "app/state/trading_state_manager.py", "SETTLE",
      f"realized=Rs.{snap['pnl']['realized']:.0f} available=Rs.{snap['capital']['available']:.0f} "
      f"deployed=Rs.{snap['capital']['deployed']:.0f} open_positions={snap['position_count']}")

# ── 15. DASHBOARD (single source of truth read) ──────────────────────────────
db = SessionLocal()
try:
    dash = dashboard_service.snapshot(db)
finally:
    db.close()
risk_guard.check_entry = _saved  # restore
consistent = abs(dash.pnl.realized_pnl - snap['pnl']['realized']) < 1e-6
stage(15, "Dashboard / API", "app/dashboard/dashboard_service.py", "READ",
      f"funds.broker={dash.funds.broker} realized=Rs.{dash.pnl.realized_pnl:.0f} "
      f"available=Rs.{dash.funds.available_cash:.0f} | dashboard==TSM consistent: {consistent}")
stage(16, "WebSocket / Frontend", "app/websocket/websocket_router.py", "PUSH",
      "/ws/state pushes the same snapshot (version-gated); Portfolio/Positions/Journal read it")

# ── capture the concrete trade record for the Excel 'Trade Detail' sheet ─────
TRADE = {
    "symbol": SYMBOL, "exchange": EXCH, "side": "BUY", "style": "INTRADAY",
    "qty": QTY, "entry_price": ENTRY, "stop_loss": SL, "target": TP,
    "broker_order_id": resp.order_id, "sl_order_id": resp.sl_order_id,
    "fill_status": resp.fill_status, "exit_price": LTP, "exit_reason": "TARGET_HIT",
    "realized_pnl": close_pnl, "data_quality": tech.data_quality,
    "rsi": tech.rsi, "ema_9": tech.ema_9, "ema_21": tech.ema_21,
    "vwap": tech.vwap, "atr": tech.atr, "supertrend": tech.supertrend,
    "ai_signal": AI_SIGNAL, "ai_confidence": AI_CONF,
    "capital_before": 500000.0, "capital_after": snap['capital']['available'],
}

out = {"generated": datetime.now().isoformat(timespec="seconds"),
       "mode": "OFFLINE DEMO (fake broker, market closed)", "stages": STAGES, "trade": TRADE}
with open("flow_capture.json", "w", encoding="utf-8") as f:
    json.dump(out, f, indent=2)

print("=" * 78)
print("CV6 COMPLETE TRADE LIFECYCLE — CAPTURED (offline demo, fake broker)")
print("=" * 78)
for s in STAGES:
    print(f"{s['step']:>2}. [{s['event']:<8}] {s['stage']:<26} {s['owner_file']}")
    print(f"      {s['detail']}")
print("=" * 78)
print(f"TRADE: BUY {QTY} {SYMBOL} @ {ENTRY} -> exit {LTP} ({TRADE['exit_reason']}) "
      f"= Rs.{close_pnl:.0f} | capital {TRADE['capital_before']:.0f} -> {TRADE['capital_after']:.0f}")
print("Saved flow_capture.json")
