from __future__ import annotations

import logging
import os
import time
from pathlib import Path
from typing import Any

from .notifications import _notify_charon_replay

logger = logging.getLogger(__name__)

_DEAD_LETTER_DIR = Path("/tmp/opendeviationbar-dead-letter")
_OVERLAP_HEAL_INTERVAL_S = int(
    os.environ.get("OPENDEVIATIONBAR_OVERLAP_HEAL_INTERVAL_S", "300")
)


def _niffler_dead_letter(
    symbol: str,
    threshold: int,
    bar_dicts: list[dict[str, Any]],
) -> Path | None:
    """Niffler: serialize failed bars to a dead-letter Parquet file.

    Extracts TID range for targeted kintsugi repair if Niffler + Charon both fail.
    Returns the path written, or None on failure.
    """
    # Phase 5: Extract TID range for diagnostics
    min_first_tid = None
    max_last_tid = None
    for bd in bar_dicts:
        ftid = bd.get("first_agg_trade_id")
        ltid = bd.get("last_agg_trade_id")
        if ftid is not None:
            min_first_tid = ftid if min_first_tid is None else min(min_first_tid, ftid)
        if ltid is not None:
            max_last_tid = ltid if max_last_tid is None else max(max_last_tid, ltid)

    try:
        import polars as pl

        _DEAD_LETTER_DIR.mkdir(parents=True, exist_ok=True)
        ts = int(time.time())
        path = _DEAD_LETTER_DIR / f"{symbol}_{threshold}_{ts}.parquet"
        # Metadata keys (_symbol, _threshold) already .pop()'d at call site
        pl.DataFrame(bar_dicts).write_parquet(path)
    except (OSError, RuntimeError, ValueError):
        logger.exception(
            "niffler: failed to write dead-letter for %s@%d",
            symbol,
            threshold,
        )
        return None
    else:
        logger.warning(
            "niffler: dead-lettered %d bars to %s (tid_range=[%s, %s])",
            len(bar_dicts),
            path,
            min_first_tid,
            max_last_tid,
        )
        return path


def _niffler_notify_dead_letter(
    symbol: str, threshold: int, n_bars: int, path: Path
) -> None:
    """Niffler: send Telegram alert when bars are dead-lettered."""
    try:
        from opendeviationbar.notify.telegram import _build_context_block, send_telegram
    except ImportError:
        return
    try:
        msg = (
            "<b>NIFFLER: DEAD LETTER</b>\n"
            f"<b>Symbol:</b> {symbol}@{threshold}\n"
            f"<b>Bars lost:</b> {n_bars:,}\n"
            f"<b>Path:</b> <code>{path}</code>\n"
            "<b>Cause:</b> ClickHouse write failed after 3 retries\n"
            "<b>Replay:</b> Niffler immediate (sub-5s) + Charon backup (5 min)\n"
            '<b>Manual:</b> <code>python -c "'
            "from opendeviationbar.kintsugi import replay_dead_letters; "
            'replay_dead_letters()"</code>' + _build_context_block("sidecar")
        )
        send_telegram(msg, disable_notification=False)
    except Exception:
        logger.exception("niffler: failed to send Telegram alert")


def _niffler_immediate_replay() -> None:
    """Niffler: immediately replay dead-letter files after writing them.

    Spawned in a daemon thread so the main sidecar loop is not blocked.
    Redis write lock prevents concurrent writes. Charon remains as 5-min backup
    in case this immediate replay also fails.
    """
    try:
        from opendeviationbar.kintsugi import replay_dead_letters

        replayed = replay_dead_letters()
        if replayed > 0:
            logger.info("niffler: replayed %d dead-letter bars (immediate)", replayed)
            _notify_charon_replay(replayed, "niffler-immediate")
    except (ImportError, OSError, RuntimeError, ConnectionError):
        logger.warning(
            "niffler: immediate replay failed (Charon will retry in 5 min)",
            exc_info=True,
        )
