# engine/execution.py
from datetime import datetime, timezone
from indian_paper_trading_strategy.engine.state import load_state, save_state

from indian_paper_trading_strategy.engine.broker import Broker
from indian_paper_trading_strategy.engine.ledger import log_event, event_exists
from indian_paper_trading_strategy.engine.db import run_with_retry
from indian_paper_trading_strategy.engine.time_utils import compute_logical_time

def _as_float(value):
    if hasattr(value, "item"):
        try:
            return float(value.item())
        except Exception:
            pass
    if hasattr(value, "iloc"):
        try:
            return float(value.iloc[-1])
        except Exception:
            pass
    return float(value)

def _local_tz():
    return datetime.now().astimezone().tzinfo

def try_execute_sip(
    now,
    market_open,
    sip_interval,
    sip_amount,
    sp_price,
    gd_price,
    eq_w,
    gd_w,
    broker: Broker | None = None,
    mode: str | None = "LIVE",
):
    def _op(cur, _conn):
        if now.tzinfo is None:
            now_ts = now.replace(tzinfo=_local_tz())
        else:
            now_ts = now
        event_ts = now_ts
        log_event("DEBUG_ENTER_TRY_EXECUTE", {
            "now": now_ts.isoformat(),
        }, cur=cur, ts=event_ts)

        state = load_state(mode=mode, cur=cur, for_update=True)

        force_execute = state.get("last_sip_ts") is None

        if not market_open:
            return state, False

        last = state.get("last_sip_ts") or state.get("last_run")
        if last and not force_execute:
            try:
                last_dt = datetime.fromisoformat(last)
            except ValueError:
                last_dt = None
            if last_dt:
                if last_dt.tzinfo is None:
                    last_dt = last_dt.replace(tzinfo=_local_tz())
                if now_ts.tzinfo and last_dt.tzinfo and last_dt.tzinfo != now_ts.tzinfo:
                    last_dt = last_dt.astimezone(now_ts.tzinfo)
            if last_dt and (now_ts - last_dt).total_seconds() < sip_interval:
                return state, False

        logical_time = compute_logical_time(now_ts, last, sip_interval)
        if event_exists("SIP_EXECUTED", logical_time, cur=cur):
            return state, False

        sp_price_val = _as_float(sp_price)
        gd_price_val = _as_float(gd_price)
        eq_w_val = _as_float(eq_w)
        gd_w_val = _as_float(gd_w)
        sip_amount_val = _as_float(sip_amount)

        nifty_qty = (sip_amount_val * eq_w_val) / sp_price_val
        gold_qty = (sip_amount_val * gd_w_val) / gd_price_val

        if broker is None:
            return state, False

        funds = broker.get_funds(cur=cur)
        cash = funds.get("cash")
        if cash is not None and float(cash) < sip_amount_val:
            return state, False

        log_event("DEBUG_EXECUTION_DECISION", {
            "force_execute": force_execute,
            "last_sip_ts": state.get("last_sip_ts"),
            "now": now_ts.isoformat(),
        }, cur=cur, ts=event_ts)

        nifty_order = broker.place_order(
            "NIFTYBEES.NS",
            "BUY",
            nifty_qty,
            sp_price_val,
            cur=cur,
            logical_time=logical_time,
        )
        gold_order = broker.place_order(
            "GOLDBEES.NS",
            "BUY",
            gold_qty,
            gd_price_val,
            cur=cur,
            logical_time=logical_time,
        )
        orders = [nifty_order, gold_order]
        executed = all(
            isinstance(order, dict) and order.get("status") == "FILLED"
            for order in orders
        )
        if not executed:
            return state, False
        assert len(orders) > 0, "executed=True but no broker orders placed"

        funds_after = broker.get_funds(cur=cur)
        cash_after = funds_after.get("cash")
        if cash_after is not None:
            state["cash"] = float(cash_after)

        state["nifty_units"] += nifty_qty
        state["gold_units"] += gold_qty
        state["total_invested"] += sip_amount_val
        state["last_sip_ts"] = now_ts.isoformat()
        state["last_run"] = now_ts.isoformat()

        save_state(
            state,
            mode=mode,
            cur=cur,
            emit_event=True,
            event_meta={"source": "sip"},
        )

        log_event(
            "SIP_EXECUTED",
            {
                "nifty_units": nifty_qty,
                "gold_units": gold_qty,
                "nifty_price": sp_price_val,
                "gold_price": gd_price_val,
                "amount": sip_amount_val,
            },
            cur=cur,
            ts=event_ts,
            logical_time=logical_time,
        )

        return state, True

    return run_with_retry(_op)

