"""Pure functions for rule loading, filtering, updating, and formatting.

No fastmcp dependency — this module is easily testable without async or MCP context.
"""

from __future__ import annotations

import json
import logging
import os
import re
import subprocess
import tempfile
import urllib.error
import urllib.request
from datetime import datetime, timezone
from pathlib import Path

logger = logging.getLogger("quickcall.recs")


def load_rules(base_dir: Path, repo_slug: str) -> dict | None:
    """Read rules.json for a repo. Returns parsed dict or None."""
    rules_path = base_dir / "recs" / repo_slug / "rules.json"
    if not rules_path.exists():
        return None
    try:
        return json.loads(rules_path.read_text())
    except (json.JSONDecodeError, OSError):
        logger.warning("Failed to read rules from %s", rules_path)
        return None


def get_pending_rules(rules_data: dict) -> list[dict]:
    """Filter rules with status 'pending' or 'open'."""
    return [r for r in rules_data.get("rules", []) if r.get("status") in ("pending", "open")]


def get_accepted_rules(rules_data: dict) -> list[dict]:
    """Filter rules with status 'accepted'."""
    return [r for r in rules_data.get("rules", []) if r.get("status") == "accepted"]


def resolve_repo_slug(project_dir: str) -> str | None:
    """Extract repo slug from git remote in project_dir. Returns 'org--repo' or None."""
    try:
        result = subprocess.run(
            ["git", "-C", project_dir, "remote", "get-url", "origin"],
            capture_output=True,
            text=True,
            timeout=5,
        )
        if result.returncode != 0:
            return None
        remote = result.stdout.strip()
    except (subprocess.TimeoutExpired, FileNotFoundError, OSError):
        return None

    # Extract org/repo from SSH (git@github.com:org/repo.git) or HTTPS (https://github.com/org/repo.git)
    match = re.search(r"[:/]([^/]+/[^/]+?)(?:\.git)?$", remote)
    if not match:
        return None
    return match.group(1).replace("/", "--")


def update_rule_status(base_dir: Path, repo_slug: str, rule_id: str, new_status: str) -> bool:
    """Update a rule's status in rules.json. Returns True if found and updated."""
    rules_path = base_dir / "recs" / repo_slug / "rules.json"
    if not rules_path.exists():
        return False
    try:
        data = json.loads(rules_path.read_text())
    except (json.JSONDecodeError, OSError):
        return False

    found = False
    for rule in data.get("rules", []):
        if rule.get("id") == rule_id:
            rule["status"] = new_status
            if new_status == "accepted":
                rule["accepted_at"] = datetime.now(timezone.utc).isoformat()
            found = True
            break

    if not found:
        return False

    # Atomic write: tempfile + os.replace
    fd, tmp_path = tempfile.mkstemp(dir=str(rules_path.parent), suffix=".tmp")
    try:
        with os.fdopen(fd, "w") as f:
            json.dump(data, f, indent=2)
            f.write("\n")
        os.replace(tmp_path, str(rules_path))
    except BaseException:
        try:
            os.unlink(tmp_path)
        except OSError:
            pass
        raise
    return True


def format_accepted_rules(rules_data: dict) -> str:
    """Format accepted rules for AI context injection."""
    accepted = get_accepted_rules(rules_data)
    if not accepted:
        return ""

    repo = rules_data.get("repo", "unknown")
    lines = [f"[QuickCall Rules for {repo}]", "", "FOLLOW THESE RULES:"]
    for rule in accepted:
        lines.append(f"- {rule['rule']}")
    return "\n".join(lines)


def send_feedback(
    platform_url: str,
    platform_token: str,
    rec_id: str,
    rating: str,
    comment: str | None = None,
) -> bool:
    """POST feedback to platform API. Returns True on success."""
    url = f"{platform_url.rstrip('/')}/api/cli/recs/{rec_id}/feedback"
    body = json.dumps({"rating": rating, "comment": comment}).encode()
    req = urllib.request.Request(url, data=body, method="POST")
    req.add_header("Authorization", f"Bearer {platform_token}")
    req.add_header("Content-Type", "application/json")
    try:
        with urllib.request.urlopen(req, timeout=10) as resp:
            return resp.status < 300
    except (urllib.error.URLError, OSError, TimeoutError):
        logger.warning("Failed to send feedback for %s", rec_id)
        return False
