"""Repair signal file for sidecar->kintsugi handoff (STARTUP-02).

The sidecar writes a signal file listing symbols with broken yesterday data.
Kintsugi reads the signal file on its next pass and prioritizes those symbols.

The signal file is persistent (/var/tmp survives reboots on Linux) so it
survives kintsugi restarts. Kintsugi deletes the file after processing (Phase 2).
"""

from __future__ import annotations

import contextlib
import json
import logging
import os
import tempfile
from datetime import UTC, datetime
from pathlib import Path

logger = logging.getLogger(__name__)

SIGNAL_FILE_PATH = "/var/tmp/opendeviationbar-yesterday-repair.json"


def write_yesterday_signal(
    flagged: dict[str, str],
    sidecar_pid: int | None = None,
) -> None:
    """Write repair signal file atomically.

    Parameters
    ----------
    flagged : dict[str, str]
        Mapping of {symbol: reason} where reason is one of:
        "orphan_misaligned", "orphan_missing", "stathera_gap",
        "stathera_overlap", "ch_unavailable".
    sidecar_pid : int | None
        Override PID (default: current process PID).
    """
    payload = {
        "symbols": flagged,
        "timestamp": datetime.now(UTC).isoformat(),
        "sidecar_pid": sidecar_pid if sidecar_pid is not None else os.getpid(),
    }

    signal_dir = str(Path(SIGNAL_FILE_PATH).parent)

    # Atomic write: tempfile in same directory + rename
    fd = None
    tmp_path = None
    try:
        fd, tmp_path = tempfile.mkstemp(dir=signal_dir, suffix=".tmp")
        with os.fdopen(fd, "w") as f:
            fd = None  # os.fdopen takes ownership
            json.dump(payload, f, indent=2)
        Path(tmp_path).rename(SIGNAL_FILE_PATH)
        tmp_path = None  # rename succeeded, no cleanup needed
        logger.info(
            "repair signal: wrote %d flagged symbols to %s",
            len(flagged),
            SIGNAL_FILE_PATH,
        )
    except Exception:
        logger.warning(
            "repair signal: failed to write %s", SIGNAL_FILE_PATH, exc_info=True
        )
        # Clean up temp file if rename didn't happen
        if tmp_path is not None:
            with contextlib.suppress(OSError):
                Path(tmp_path).unlink()
    finally:
        if fd is not None:
            with contextlib.suppress(OSError):
                os.close(fd)


def read_yesterday_signal() -> dict | None:
    """Read repair signal file.

    Returns parsed JSON dict or None if file does not exist.
    Does NOT delete the file (Phase 2 kintsugi handles deletion).
    """
    try:
        with Path(SIGNAL_FILE_PATH).open() as f:
            return json.load(f)
    except FileNotFoundError:
        return None
    except Exception:
        logger.warning(
            "repair signal: failed to read %s", SIGNAL_FILE_PATH, exc_info=True
        )
        return None


def clear_yesterday_signal() -> None:
    """Delete repair signal file (race-safe).

    Called by kintsugi after processing all flagged shards (STARTUP-03).
    No-op if file does not exist.
    """
    try:
        Path(SIGNAL_FILE_PATH).unlink()
        logger.info("repair signal: cleared %s", SIGNAL_FILE_PATH)
    except FileNotFoundError:
        pass  # Race-safe: file already deleted
    except Exception:
        logger.warning(
            "repair signal: failed to clear %s", SIGNAL_FILE_PATH, exc_info=True
        )
