"""Journal persistant des trades (NinjaTrader, paper, etc.)."""

from __future__ import annotations

import json
import uuid
from datetime import datetime, timedelta, timezone
from pathlib import Path

from runtime.paths import user_data_dir

_LEDGER_FILE = user_data_dir() / "trade_ledger.json"
RESERVED_OPEN_TTL = timedelta(seconds=90)


def _load() -> list[dict]:
    if not _LEDGER_FILE.is_file():
        return []
    try:
        with _LEDGER_FILE.open(encoding="utf-8") as handle:
            data = json.load(handle)
    except (json.JSONDecodeError, OSError):
        return []
    return data if isinstance(data, list) else []


def _save(rows: list[dict]) -> None:
    _LEDGER_FILE.parent.mkdir(parents=True, exist_ok=True)
    with _LEDGER_FILE.open("w", encoding="utf-8") as handle:
        json.dump(rows, handle, indent=2)


def ledger_path() -> Path:
    return _LEDGER_FILE


def history() -> list[dict]:
    return list(_load())


def clear_history() -> None:
    """Tests uniquement."""
    _save([])


def _find_duplicate(rows: list[dict], event: str, external_id: str | None) -> dict | None:
    if not external_id:
        return None
    for row in reversed(rows):
        if row.get("external_id") == external_id and row.get("event") == event:
            return row
    return None


def record_event(
    *,
    event: str,
    ticker: str,
    side: str,
    quantity: float,
    price: float,
    source: str = "unknown",
    pnl: float | None = None,
    external_id: str | None = None,
    account: str | None = None,
    signal_id: str | None = None,
) -> dict:
    rows = _load()
    existing = _find_duplicate(rows, event, external_id)
    if existing is not None:
        return existing

    row: dict = {
        "event": event,
        "ticker": ticker,
        "side": side,
        "quantity": quantity,
        "price": price,
        "source": source,
        "at": datetime.now(timezone.utc).isoformat(),
    }
    if pnl is not None:
        row["pnl"] = pnl
    if external_id:
        row["external_id"] = external_id
    if account:
        row["account"] = account
    if signal_id:
        row["signal_id"] = signal_id

    rows.append(row)
    _save(rows)
    return row


def reserve_check_open(
    *,
    ticker: str,
    side: str,
    quantity: float,
    source: str,
    account: str | None = None,
) -> dict:
    """Reserve un slot max-trades des qu'un check autorise une entree (anti double-clic)."""
    return record_event(
        event="open_reserved",
        ticker=ticker,
        side=side,
        quantity=quantity,
        price=0.0,
        source=source,
        external_id=f"chk-{uuid.uuid4().hex}",
        account=account,
    )


def consume_reserved_open(*, ticker: str, account: str | None = None) -> bool:
    """Retire une reservation quand le fill open arrive (evite double comptage)."""
    rows = _load()
    now = datetime.now(timezone.utc)
    match_index: int | None = None
    for index in range(len(rows) - 1, -1, -1):
        row = rows[index]
        if row.get("event") != "open_reserved":
            continue
        if row.get("ticker") != ticker:
            continue
        if account and row.get("account") and row.get("account") != account:
            continue
        at = datetime.fromisoformat(row["at"])
        if at.tzinfo is None:
            at = at.replace(tzinfo=timezone.utc)
        if now - at > RESERVED_OPEN_TTL:
            continue
        match_index = index
        break

    if match_index is None:
        return False

    rows.pop(match_index)
    _save(rows)
    return True


def prune_expired_reserved() -> int:
    rows = _load()
    now = datetime.now(timezone.utc)
    kept: list[dict] = []
    removed = 0
    for row in rows:
        if row.get("event") != "open_reserved":
            kept.append(row)
            continue
        at = datetime.fromisoformat(row["at"])
        if at.tzinfo is None:
            at = at.replace(tzinfo=timezone.utc)
        if now - at > RESERVED_OPEN_TTL:
            removed += 1
            continue
        kept.append(row)
    if removed:
        _save(kept)
    return removed
