"""
Failure buffer for active-claude-github-mcp.

When a GitHub operation fails after retries, the pending operation
is written to .claude/pending-posts.md so it can be replayed on
the next session start.

Fail-fast principle: buffer writes that fail raise immediately.
A failed buffer write is worse than a missing record — it means
we don't know what was lost.
"""

from __future__ import annotations

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


BUFFER_FILENAME = ".claude/pending-posts.md"


class BufferError(RuntimeError):
    """Raised when the buffer file cannot be written."""


def _buffer_path(project_dir: Path) -> Path:
    return project_dir / BUFFER_FILENAME


def write_pending(
    project_dir: Path,
    tool_name: str,
    tool_args: dict,
    error_message: str,
) -> Path:
    """
    Append a failed operation to the pending buffer.

    Args:
        project_dir: project root (where .claude/ lives)
        tool_name: name of the MCP tool that failed
        tool_args: arguments that were passed to the tool
        error_message: the error that caused the failure

    Returns the path to the buffer file.
    Raises BufferError if the write fails.
    """
    buffer_path = _buffer_path(project_dir)

    try:
        buffer_path.parent.mkdir(parents=True, exist_ok=True)
    except OSError as exc:
        raise BufferError(
            f"Cannot create .claude/ directory: {exc}"
        ) from exc

    timestamp = datetime.now(timezone.utc).isoformat()
    entry = (
        f"\n## Pending — {timestamp}\n"
        f"**Tool:** `{tool_name}`\n"
        f"**Error:** {error_message}\n"
        f"**Args:**\n```json\n{json.dumps(tool_args, indent=2)}\n```\n"
    )

    try:
        with buffer_path.open("a", encoding="utf-8") as f:
            f.write(entry)
    except OSError as exc:
        raise BufferError(
            f"Cannot write to pending buffer {buffer_path}: {exc}"
        ) from exc

    return buffer_path


def read_pending(project_dir: Path) -> list[dict]:
    """
    Read all pending operations from the buffer.

    Returns a list of {"tool": str, "args": dict, "timestamp": str,
    "error": str} dicts. Returns [] if no buffer file exists.

    Does NOT clear the buffer — call clear_pending() after
    successful replay.
    """
    buffer_path = _buffer_path(project_dir)

    if not buffer_path.exists():
        return []

    try:
        content = buffer_path.read_text(encoding="utf-8")
    except OSError as exc:
        raise BufferError(
            f"Cannot read pending buffer {buffer_path}: {exc}"
        ) from exc

    pending = []
    for section in content.split("\n## Pending — "):
        section = section.strip()
        if not section:
            continue

        lines = section.split("\n")
        timestamp_line = lines[0].strip()
        result: dict = {"timestamp": timestamp_line}

        # Parse tool name
        for line in lines:
            if line.startswith("**Tool:**"):
                result["tool"] = line.replace("**Tool:**", "").strip().strip("`")
            elif line.startswith("**Error:**"):
                result["error"] = line.replace("**Error:**", "").strip()

        # Parse args JSON block
        try:
            json_start = section.index("```json\n") + len("```json\n")
            json_end = section.index("\n```", json_start)
            result["args"] = json.loads(section[json_start:json_end])
        except (ValueError, json.JSONDecodeError):
            result["args"] = {}

        if "tool" in result:
            pending.append(result)

    return pending


def clear_pending(project_dir: Path) -> None:
    """
    Delete the pending buffer file after successful replay.

    Raises BufferError if the file exists but cannot be deleted.
    Silently succeeds if the file does not exist.
    """
    buffer_path = _buffer_path(project_dir)

    if not buffer_path.exists():
        return

    try:
        buffer_path.unlink()
    except OSError as exc:
        raise BufferError(
            f"Cannot delete pending buffer {buffer_path}: {exc}"
        ) from exc


def has_pending(project_dir: Path) -> bool:
    """Return True if the buffer contains at least one pending operation.

    File existence alone is not sufficient — a 0-byte or whitespace-only
    file is treated as no pending operations.
    """
    buffer_path = _buffer_path(project_dir)
    if not buffer_path.exists():
        return False
    try:
        content = buffer_path.read_text(encoding="utf-8")
    except OSError:
        return False
    return "## Pending —" in content
