"""agentkit user-badge engine — generate shields.io badge for a GitHub user's agent-readiness grade."""
from __future__ import annotations

import json
from dataclasses import dataclass
from typing import Optional
from urllib.parse import quote


# ---------------------------------------------------------------------------
# Badge grade thresholds — contract spec: A≥90, B≥75, C≥60, D≥45, F<45
# ---------------------------------------------------------------------------

BADGE_GRADE_THRESHOLDS = [
    (90.0, "A", "brightgreen"),
    (75.0, "B", "green"),
    (60.0, "C", "yellow"),
    (45.0, "D", "orange"),
    (0.0, "F", "red"),
]

PYPI_LINK = "https://pypi.org/project/agentkit-cli/"
BADGE_LABEL = "agent-readiness"

# Sentinel markers for README injection
BADGE_START_SENTINEL = "<!-- agentkit-user-badge-start -->"
BADGE_END_SENTINEL = "<!-- agentkit-user-badge-end -->"


def score_to_badge_grade(score: float) -> tuple[str, str]:
    """Return (grade, color) for a given numeric score."""
    for threshold, grade, color in BADGE_GRADE_THRESHOLDS:
        if score >= threshold:
            return grade, color
    return "F", "red"


@dataclass
class UserBadgeResult:
    username: str
    score: float
    grade: str
    badge_url: str
    badge_markdown: str
    readme_snippet: str

    def to_dict(self) -> dict:
        return {
            "username": self.username,
            "score": self.score,
            "grade": self.grade,
            "badge_url": self.badge_url,
            "badge_markdown": self.badge_markdown,
            "readme_snippet": self.readme_snippet,
        }

    def to_anybadge_json(self) -> dict:
        """Return anybadge-compatible JSON for local badge generation."""
        return {
            "label": BADGE_LABEL,
            "value": f"{self.grade} ({self.score:.0f}/100)",
            "color": _grade_to_color(self.grade),
            "style": "flat-square",
        }


def _grade_to_color(grade: str) -> str:
    for _, g, color in BADGE_GRADE_THRESHOLDS:
        if g == grade:
            return color
    return "red"


class UserBadgeEngine:
    """Generate shields.io badge URLs and README snippets for agent-readiness scores."""

    def generate_badge_url(self, user: str, score: float, grade: str) -> str:
        """Generate a shields.io static badge URL."""
        color = _grade_to_color(grade)
        # Encode message: e.g. "A (85/100)"
        message = f"{grade} ({score:.0f}/100)"
        # shields.io requires specific encoding: spaces→%20, /→%2F, (→%28, )→%29
        encoded_label = quote(BADGE_LABEL, safe="")
        encoded_message = (
            message
            .replace("-", "--")  # shields.io: single dash in message needs escaping
            .replace(" ", "%20")
            .replace("/", "%2F")
            .replace("(", "%28")
            .replace(")", "%29")
        )
        return (
            f"https://img.shields.io/badge/{encoded_label}-{encoded_message}-{color}"
            f"?style=flat-square"
        )

    def generate_markdown(self, user: str, score: float, grade: str) -> str:
        """Generate README badge markdown with PyPI link."""
        badge_url = self.generate_badge_url(user, score, grade)
        return f"[![Agent Readiness]({badge_url})]({PYPI_LINK})"

    def generate_readme_snippet(self, user: str, score: float, grade: str) -> str:
        """Generate full README snippet with badge + link."""
        badge_md = self.generate_markdown(user, score, grade)
        return (
            f"{badge_md}\n\n"
            f"<!-- Generated by [agentkit-cli]({PYPI_LINK}) for @{user} -->"
        )

    def generate_json(self, user: str, score: float, grade: str) -> dict:
        """Generate full JSON payload including anybadge-compatible format."""
        badge_url = self.generate_badge_url(user, score, grade)
        badge_md = self.generate_markdown(user, score, grade)
        snippet = self.generate_readme_snippet(user, score, grade)
        result = UserBadgeResult(
            username=user,
            score=score,
            grade=grade,
            badge_url=badge_url,
            badge_markdown=badge_md,
            readme_snippet=snippet,
        )
        out = result.to_dict()
        out["anybadge"] = result.to_anybadge_json()
        return out

    def run(self, user: str, score: float, grade: Optional[str] = None) -> UserBadgeResult:
        """Build a complete UserBadgeResult for a user/score."""
        if grade is None:
            grade, _ = score_to_badge_grade(score)
        badge_url = self.generate_badge_url(user, score, grade)
        badge_md = self.generate_markdown(user, score, grade)
        snippet = self.generate_readme_snippet(user, score, grade)
        return UserBadgeResult(
            username=user,
            score=score,
            grade=grade,
            badge_url=badge_url,
            badge_markdown=badge_md,
            readme_snippet=snippet,
        )


# ---------------------------------------------------------------------------
# README inject helpers
# ---------------------------------------------------------------------------

def _find_sentinel_bounds(lines: list[str]) -> Optional[tuple[int, int]]:
    """Return (start_idx, end_idx) of sentinel block, or None."""
    start = None
    for i, line in enumerate(lines):
        if line.strip() == BADGE_START_SENTINEL:
            start = i
        elif line.strip() == BADGE_END_SENTINEL and start is not None:
            return start, i
    return None


def _find_insert_point(lines: list[str]) -> int:
    """Return line index after any H1 title, or 0."""
    for i, line in enumerate(lines):
        stripped = line.strip()
        if stripped.startswith("# ") and not stripped.startswith("## "):
            return i + 1
    return 0


def inject_badge_into_readme(content: str, badge_markdown: str, dry_run: bool = False) -> str:
    """Inject or update the badge section in README content. Idempotent."""
    lines = content.splitlines(keepends=True)
    badge_block = (
        f"{BADGE_START_SENTINEL}\n"
        f"{badge_markdown}\n"
        f"{BADGE_END_SENTINEL}\n"
    )
    bounds = _find_sentinel_bounds(lines)
    if bounds is not None:
        start, end = bounds
        # Replace existing block
        new_lines = lines[:start] + [badge_block] + lines[end + 1:]
    else:
        insert_at = _find_insert_point(lines)
        new_lines = lines[:insert_at] + ["\n", badge_block, "\n"] + lines[insert_at:]
    return "".join(new_lines)
