from datetime import datetime, timezone
from pathlib import Path

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

ENGINE_ROOT = Path(__file__).resolve().parents[1]
STORAGE_DIR = ENGINE_ROOT / "storage"
MTM_FILE = STORAGE_DIR / "mtm_ledger.csv"

MTM_INTERVAL_SECONDS = 60

def _log_mtm_in_tx(
    cur,
    nifty_units,
    gold_units,
    nifty_price,
    gold_price,
    total_invested,
    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)
    nifty_value = nifty_units * nifty_price
    gold_value = gold_units * gold_price
    portfolio_value = nifty_value + gold_value
    pnl = portfolio_value - total_invested

    row = {
        "timestamp": ts.isoformat(),
        "logical_time": logical_ts.isoformat(),
        "nifty_units": nifty_units,
        "gold_units": gold_units,
        "nifty_price": nifty_price,
        "gold_price": gold_price,
        "nifty_value": nifty_value,
        "gold_value": gold_value,
        "portfolio_value": portfolio_value,
        "total_invested": total_invested,
        "pnl": pnl,
    }
    cur.execute(
        """
        INSERT INTO mtm_ledger (
            user_id,
            run_id,
            timestamp,
            logical_time,
            nifty_units,
            gold_units,
            nifty_price,
            gold_price,
            nifty_value,
            gold_value,
            portfolio_value,
            total_invested,
            pnl
        )
        VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
        ON CONFLICT DO NOTHING
        """,
        (
            scope_user,
            scope_run,
            ts,
            logical_ts,
            row["nifty_units"],
            row["gold_units"],
            row["nifty_price"],
            row["gold_price"],
            row["nifty_value"],
            row["gold_value"],
            row["portfolio_value"],
            row["total_invested"],
            row["pnl"],
        ),
    )
    if cur.rowcount:
        insert_engine_event(cur, "MTM_UPDATED", data=row, ts=ts)
    return portfolio_value, pnl

def log_mtm(
    nifty_units,
    gold_units,
    nifty_price,
    gold_price,
    total_invested,
    *,
    cur=None,
    logical_time=None,
    user_id: str | None = None,
    run_id: str | None = None,
):
    ts = logical_time or datetime.now(timezone.utc)
    if cur is not None:
        return _log_mtm_in_tx(
            cur,
            nifty_units,
            gold_units,
            nifty_price,
            gold_price,
            total_invested,
            ts,
            logical_time=logical_time,
            user_id=user_id,
            run_id=run_id,
        )

    def _op(cur, _conn):
        return _log_mtm_in_tx(
            cur,
            nifty_units,
            gold_units,
            nifty_price,
            gold_price,
            total_invested,
            ts,
            logical_time=logical_time,
            user_id=user_id,
            run_id=run_id,
        )

    return run_with_retry(_op)

def _get_last_mtm_ts(user_id: str | None = None, run_id: str | None = None):
    scope_user, scope_run = get_context(user_id, run_id)
    with db_connection() as conn:
        with conn.cursor() as cur:
            cur.execute(
                "SELECT MAX(timestamp) FROM mtm_ledger WHERE user_id = %s AND run_id = %s",
                (scope_user, scope_run),
            )
            row = cur.fetchone()
            if not row or row[0] is None:
                return None
            return row[0].astimezone().replace(tzinfo=None)

def should_log_mtm(df, now, user_id: str | None = None, run_id: str | None = None):
    if df is None:
        last_ts = _get_last_mtm_ts(user_id=user_id, run_id=run_id)
        if last_ts is None:
            return True
        return (now - last_ts).total_seconds() >= MTM_INTERVAL_SECONDS
    if getattr(df, "empty", False):
        return True
    try:
        last_ts = datetime.fromisoformat(str(df.iloc[-1]["timestamp"]))
    except Exception:
        return True
    return (now - last_ts).total_seconds() >= MTM_INTERVAL_SECONDS

