"""
DeepSweep Reputation API Client
================================

Manages communication with the reputation service for scores, streaks, and social sharing.
Implements offline-first architecture with graceful degradation.

Architecture:
- Online mode: Submit validations to API for global leaderboard participation
- Offline mode: Local score calculation using OfflineScorer
- Fire-and-forget: API calls never block CLI execution
- Silent failure: Network errors gracefully fall back to offline mode

Privacy:
- Only anonymized data transmitted (install_id prefix, scores, grades)
- No source code, file paths, or repository names
- Opt-out: --offline flag or DEEPSWEEP_OFFLINE=1

API Endpoints:
- POST /v1/reputation/submit - Submit validation for reputation scoring
- POST /v1/reputation/share - Generate social share link
- GET /v1/reputation/leaderboard - Get global leaderboard (future)
"""

import json
import os
from dataclasses import dataclass, field
from datetime import datetime, timezone
from typing import Any, Final
from urllib.parse import urlencode, urlparse
from urllib.request import Request, urlopen

from deepsweep.constants import VERSION
from deepsweep.reputation.local_store import LocalReputationStore

# Reputation API base URL
REPUTATION_API_BASE: Final[str] = os.environ.get(
    "DEEPSWEEP_REPUTATION_API", "https://api.deepsweep.ai/v1/reputation"
)

# Request timeout (never block CLI)
REQUEST_TIMEOUT: Final[float] = 2.0

# Tier thresholds (score-based)
TIER_THRESHOLDS: Final[dict[str, int]] = {
    "unverified": 0,
    "bronze": 60,
    "silver": 70,
    "gold": 80,
    "platinum": 90,
    "diamond": 95,
}


@dataclass
class ScoreResponse:
    """Response from reputation API after validation submission."""

    score: int
    tier: str
    streak_days: int
    global_rank: int | None = None
    percentile: int | None = None
    tier_progress: dict[str, int] = field(default_factory=dict)


@dataclass
class ShareResponse:
    """Response from share link generation API."""

    share_url: str
    share_text: str
    badge_url: str


class OfflineScorer:
    """
    Local score calculation when API is unavailable.

    Implements the same scoring algorithm as the server to ensure
    consistent results in offline mode.
    """

    @staticmethod
    def calculate_score(grade: str, finding_count: int = 0) -> int:
        """
        Calculate security score from grade and finding count.

        Algorithm:
        - Grade A (90-100): 100 - (finding_count * 2)
        - Grade B (80-89): 85 - (finding_count * 3)
        - Grade C (70-79): 75 - (finding_count * 4)
        - Grade D (60-69): 65 - (finding_count * 5)
        - Grade F (0-59): 50 - (finding_count * 5)

        Args:
            grade: Letter grade (A-F)
            finding_count: Number of security findings

        Returns:
            Score (0-100)
        """
        base_scores = {"A": 100, "B": 85, "C": 75, "D": 65, "F": 50}
        penalties = {"A": 2, "B": 3, "C": 4, "D": 5, "F": 5}

        base = base_scores.get(grade.upper(), 50)
        penalty = penalties.get(grade.upper(), 5) * finding_count

        return max(0, min(100, base - penalty))

    @staticmethod
    def calculate_tier(score: int) -> str:
        """
        Calculate tier from score.

        Tiers:
        - Diamond: 95+
        - Platinum: 90-94
        - Gold: 80-89
        - Silver: 70-79
        - Bronze: 60-69
        - Unverified: 0-59

        Args:
            score: Security score (0-100)

        Returns:
            Tier name
        """
        if score >= 95:
            return "diamond"
        elif score >= 90:
            return "platinum"
        elif score >= 80:
            return "gold"
        elif score >= 70:
            return "silver"
        elif score >= 60:
            return "bronze"
        else:
            return "unverified"


class ReputationAPIClient:
    """
    Client for DeepSweep Reputation API.

    Handles validation submission, score retrieval, and social sharing.
    Gracefully degrades to offline mode when API is unavailable.
    """

    def __init__(
        self,
        offline_mode: bool = False,
        install_id: str | None = None,
        api_base: str | None = None,
    ):
        """
        Initialize reputation API client.

        Args:
            offline_mode: If True, skip all API calls
            install_id: Override install ID (for testing)
            api_base: Override API base URL (for testing)
        """
        self.offline_mode = offline_mode or os.environ.get("DEEPSWEEP_OFFLINE") == "1"
        self.api_base = api_base or REPUTATION_API_BASE
        self.install_id = install_id
        self.store = LocalReputationStore()

    def _make_request(
        self,
        endpoint: str,
        method: str = "POST",
        data: dict[str, Any] | None = None,
        timeout: float = REQUEST_TIMEOUT,
    ) -> dict[str, Any] | None:
        """
        Make HTTP request to reputation API.

        Args:
            endpoint: API endpoint (e.g., "submit")
            method: HTTP method
            data: Request payload
            timeout: Request timeout in seconds

        Returns:
            Response JSON or None on failure
        """
        if self.offline_mode:
            return None

        url = f"{self.api_base}/{endpoint}"

        # Validate URL scheme
        parsed = urlparse(url)
        if parsed.scheme not in ("http", "https"):
            return None

        try:
            headers = {
                "Content-Type": "application/json",
                "User-Agent": f"deepsweep-cli/{VERSION}",
                "X-Client-Version": VERSION,
            }

            if self.install_id:
                headers["X-Install-ID"] = self.install_id

            body = json.dumps(data).encode("utf-8") if data is not None else None

            request = Request(url, data=body, headers=headers, method=method)

            with urlopen(request, timeout=timeout) as response:  # nosec
                return json.loads(response.read().decode("utf-8"))

        except Exception:
            # Silent failure - fall back to offline mode
            return None

    def submit_validation(
        self,
        grade: str,
        finding_count: int,
        pattern_ids: list[str] | None = None,
        duration_ms: int = 0,
    ) -> ScoreResponse:
        """
        Submit validation results to reputation API.

        Args:
            grade: Letter grade (A-F)
            finding_count: Number of findings
            pattern_ids: List of triggered pattern IDs
            duration_ms: Validation duration in milliseconds

        Returns:
            ScoreResponse with score, tier, and streak info
        """
        # Calculate offline score as fallback
        offline_score = OfflineScorer.calculate_score(grade, finding_count)
        offline_tier = OfflineScorer.calculate_tier(offline_score)

        # Update local store with new streak
        streak_days = self.store.calculate_new_streak()

        # Try API submission (fire-and-forget if offline)
        response = self._make_request(
            "submit",
            data={
                "grade": grade,
                "finding_count": finding_count,
                "pattern_ids": pattern_ids or [],
                "duration_ms": duration_ms,
                "cli_version": VERSION,
                "timestamp": datetime.now(timezone.utc).isoformat(),
            },
        )

        if response and response.get("status") == "success":
            # API succeeded - use server-calculated values
            score = response.get("score", offline_score)
            tier = response.get("tier", offline_tier)
            streak_days = response.get("streak_days", streak_days)
            global_rank = response.get("global_rank")
            percentile = response.get("percentile")
            tier_progress = response.get("tier_progress", {})
        else:
            # API failed or offline - use local calculations
            score = offline_score
            tier = offline_tier
            global_rank = None
            percentile = None
            tier_progress = {}

        # Update local store
        self.store.update_after_validation(
            grade=grade,
            score=score,
            tier=tier,
            streak_days=streak_days,
        )

        return ScoreResponse(
            score=score,
            tier=tier,
            streak_days=streak_days,
            global_rank=global_rank,
            percentile=percentile,
            tier_progress=tier_progress,
        )

    def generate_share(
        self,
        grade: str,
        score: int,
        streak_days: int = 0,
    ) -> ShareResponse:
        """
        Generate social share link and text.

        Args:
            grade: Letter grade (A-F)
            score: Security score (0-100)
            streak_days: Current streak in days

        Returns:
            ShareResponse with URLs and share text
        """
        # Try API generation first
        response = self._make_request(
            "share",
            data={
                "grade": grade,
                "score": score,
                "streak_days": streak_days,
            },
        )

        if response and response.get("status") == "success":
            return ShareResponse(
                share_url=response["share_url"],
                share_text=response["share_text"],
                badge_url=response.get("badge_url", ""),
            )

        # Offline fallback - generate locally
        # Generate grade-based summary
        grade_summary = {
            "A": "All clear! Ready to ship. ✅",
            "B": "Looks good. Minor things to check.",
            "C": "Found some issues. Easy fixes.",
            "D": "Needs work. Fixing now.",
            "F": "Critical issues found. Working on it.",
        }.get(grade, "Running security checks...")

        share_text = f"""Just checked my AI-built app for security issues 🔒

Score: {score}/100 (Grade {grade})
{grade_summary}

Check your app free: deepsweep.ai

#VibeCoding #CursorAI #BuildInPublic"""

        share_params = {
            "text": share_text,
            "url": "https://deepsweep.ai",
        }

        twitter_url = f"https://twitter.com/intent/tweet?{urlencode(share_params)}"

        return ShareResponse(
            share_url=twitter_url,
            share_text=share_text,
            badge_url="https://deepsweep.ai/badge",
        )

    def get_leaderboard(self, _limit: int = 100) -> list[dict[str, Any]]:
        """
        Get global leaderboard (future feature).

        Args:
            _limit: Number of entries to retrieve (future use)

        Returns:
            List of leaderboard entries (empty if offline)
        """
        response = self._make_request("leaderboard", method="GET")

        if response and response.get("status") == "success":
            return response.get("leaderboard", [])

        return []
