# engine/ledger.py
from datetime import datetime, timezone

from indian_paper_trading_strategy.engine.db import insert_engine_event, run_with_retry, get_context
from indian_paper_trading_strategy.engine.time_utils import normalize_logical_time


def _event_exists_in_tx(cur, event, logical_time, user_id: str | None = None, run_id: str | None = None):
    scope_user, scope_run = get_context(user_id, run_id)
    logical_ts = normalize_logical_time(logical_time)
    cur.execute(
        """
        SELECT 1
        FROM event_ledger
        WHERE user_id = %s AND run_id = %s AND event = %s AND logical_time = %s
        LIMIT 1
        """,
        (scope_user, scope_run, event, logical_ts),
    )
    return cur.fetchone() is not None


def event_exists(event, logical_time, *, cur=None, user_id: str | None = None, run_id: str | None = None):
    if cur is not None:
        return _event_exists_in_tx(cur, event, logical_time, user_id=user_id, run_id=run_id)

    def _op(cur, _conn):
        return _event_exists_in_tx(cur, event, logical_time, user_id=user_id, run_id=run_id)

    return run_with_retry(_op)


def _log_event_in_tx(
    cur,
    event,
    payload,
    ts,
    logical_time=None,
    user_id: str | None = None,
    run_id: str | None = None,
):
    scope_user, scope_run = get_context(user_id, run_id)
    logical_ts = normalize_logical_time(logical_time or ts)
    cur.execute(
        """
        INSERT INTO event_ledger (
            user_id,
            run_id,
            timestamp,
            logical_time,
            event,
            nifty_units,
            gold_units,
            nifty_price,
            gold_price,
            amount
        )
        VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
        ON CONFLICT DO NOTHING
        """,
        (
            scope_user,
            scope_run,
            ts,
            logical_ts,
            event,
            payload.get("nifty_units"),
            payload.get("gold_units"),
            payload.get("nifty_price"),
            payload.get("gold_price"),
            payload.get("amount"),
        ),
    )
    if cur.rowcount:
        insert_engine_event(cur, event, data=payload, ts=ts)


def log_event(
    event,
    payload,
    *,
    cur=None,
    ts=None,
    logical_time=None,
    user_id: str | None = None,
    run_id: str | None = None,
):
    now = ts or logical_time or datetime.utcnow().replace(tzinfo=timezone.utc)
    if cur is not None:
        _log_event_in_tx(
            cur,
            event,
            payload,
            now,
            logical_time=logical_time,
            user_id=user_id,
            run_id=run_id,
        )
        return

    def _op(cur, _conn):
        _log_event_in_tx(
            cur,
            event,
            payload,
            now,
            logical_time=logical_time,
            user_id=user_id,
            run_id=run_id,
        )

    return run_with_retry(_op)

