"""Security Reputation System - Local storage for scores and streaks."""

import contextlib
import json
from dataclasses import asdict, dataclass
from datetime import date
from pathlib import Path


@dataclass
class LocalReputationData:
    """
    Locally cached reputation data.

    This data persists between validations to track progress over time.
    All data is stored locally - nothing is transmitted without explicit consent.
    """

    user_id: str | None = None
    current_score: int = 0
    tier: str = "unverified"
    streak_days: int = 0
    streak_last_date: str | None = None
    total_validations: int = 0
    first_validation_date: str | None = None
    last_grade: str | None = None
    referral_code: str | None = None

    def to_dict(self) -> dict:
        """Convert to dictionary for JSON serialization."""
        return asdict(self)

    @classmethod
    def from_dict(cls, data: dict) -> "LocalReputationData":
        """Create from dictionary (JSON deserialization)."""
        return cls(**{k: v for k, v in data.items() if k in cls.__dataclass_fields__})


class LocalReputationStore:
    """
    Manage local reputation cache.

    Stores reputation data in ~/.config/deepsweep/reputation.json
    This enables streak tracking and score history without requiring cloud sync.
    """

    CONFIG_DIR = Path.home() / ".config" / "deepsweep"
    REPUTATION_FILE = "reputation.json"

    def __init__(self):
        self.config_dir = self.CONFIG_DIR
        self.reputation_file = self.config_dir / self.REPUTATION_FILE
        self._ensure_config_dir()

    def _ensure_config_dir(self) -> None:
        """Create config directory if it doesn't exist."""
        self.config_dir.mkdir(parents=True, exist_ok=True)

    def load(self) -> LocalReputationData:
        """
        Load reputation data from disk.

        Returns:
            LocalReputationData with saved data, or empty if file doesn't exist.
        """
        if not self.reputation_file.exists():
            return LocalReputationData()

        try:
            with self.reputation_file.open() as f:
                data = json.load(f)
            return LocalReputationData.from_dict(data)
        except (json.JSONDecodeError, KeyError, OSError):
            # If file is corrupted, return empty data
            return LocalReputationData()

    def save(self, data: LocalReputationData) -> None:
        """
        Save reputation data to disk.

        Args:
            data: LocalReputationData to persist.
        """
        try:
            with self.reputation_file.open("w") as f:
                json.dump(data.to_dict(), f, indent=2)
        except OSError:
            # If we can't write, fail silently (reputation is not critical)
            pass

    def update_after_validation(
        self,
        grade: str,
        score: int,
        tier: str,
        streak_days: int,
    ) -> LocalReputationData:
        """
        Update local data after a validation.

        Args:
            grade: Letter grade (A-F)
            score: Numeric score (0-1000)
            tier: Tier name (bronze, silver, gold, etc.)
            streak_days: Current streak in days

        Returns:
            Updated LocalReputationData
        """
        data = self.load()

        today = date.today().isoformat()

        # Update fields
        data.current_score = score
        data.tier = tier
        data.streak_days = streak_days
        data.streak_last_date = today
        data.total_validations += 1
        data.last_grade = grade

        if data.first_validation_date is None:
            data.first_validation_date = today

        self.save(data)
        return data

    def get_streak_status(self) -> tuple[int, bool]:
        """
        Get current streak status.

        Returns:
            Tuple of (streak_days, is_validated_today)
        """
        data = self.load()

        if data.streak_last_date is None:
            return 0, False

        try:
            last_date = date.fromisoformat(data.streak_last_date)
        except ValueError:
            return 0, False

        today = date.today()
        days_since = (today - last_date).days

        if days_since == 0:
            return data.streak_days, True  # Validated today
        elif days_since == 1:
            return data.streak_days, False  # Streak active but not validated today
        else:
            return 0, False  # Streak broken

    def calculate_new_streak(self) -> int:
        """
        Calculate streak after a validation today.

        Returns:
            New streak count (in days)
        """
        data = self.load()

        if data.streak_last_date is None:
            return 1  # First validation ever

        try:
            last_date = date.fromisoformat(data.streak_last_date)
        except ValueError:
            return 1

        today = date.today()
        days_since = (today - last_date).days

        if days_since == 0:
            return data.streak_days  # Already validated today
        elif days_since == 1:
            return data.streak_days + 1  # Extend streak
        else:
            return 1  # Reset streak (broken)

    def clear(self) -> None:
        """Clear all reputation data (for testing/reset)."""
        if self.reputation_file.exists():
            with contextlib.suppress(OSError):
                self.reputation_file.unlink()
