"""File caching for faster repeated runs."""

import hashlib
import json
from pathlib import Path
from typing import Optional


CACHE_VERSION = "1"
CACHE_DIR = ".stablestack-cache"


def get_cache_dir(project_root: Path) -> Path:
    """Get the cache directory for a project."""
    return project_root / CACHE_DIR


def get_file_hash(path: Path) -> str:
    """Get a hash of file content and metadata."""
    try:
        stat = path.stat()
        content = path.read_bytes()
        # Hash includes content and mtime for quick invalidation
        key = f"{stat.st_mtime}:{len(content)}:{hashlib.md5(content).hexdigest()}"
        return hashlib.sha256(key.encode()).hexdigest()[:16]
    except OSError:
        return ""


class FileCache:
    """Cache for file analysis results."""

    def __init__(self, project_root: Path, enabled: bool = True):
        self.project_root = project_root
        self.enabled = enabled
        self.cache_dir = get_cache_dir(project_root)
        self.cache_file = self.cache_dir / "cache.json"
        self._cache: dict = {}
        self._dirty = False

        if enabled:
            self._load()

    def _load(self) -> None:
        """Load cache from disk."""
        if self.cache_file.exists():
            try:
                data = json.loads(self.cache_file.read_text())
                if data.get("version") == CACHE_VERSION:
                    self._cache = data.get("files", {})
            except (json.JSONDecodeError, OSError):
                self._cache = {}

    def _save(self) -> None:
        """Save cache to disk."""
        if not self._dirty or not self.enabled:
            return

        try:
            self.cache_dir.mkdir(parents=True, exist_ok=True)
            data = {
                "version": CACHE_VERSION,
                "files": self._cache,
            }
            self.cache_file.write_text(json.dumps(data, indent=2))
            self._dirty = False
        except OSError:
            pass  # Cache write failures are non-fatal

    def is_cached(self, path: Path) -> bool:
        """Check if a file's results are cached and still valid."""
        if not self.enabled:
            return False

        try:
            rel_path = str(path.relative_to(self.project_root))
        except ValueError:
            rel_path = str(path)

        cached = self._cache.get(rel_path)
        if not cached:
            return False

        current_hash = get_file_hash(path)
        return cached.get("hash") == current_hash

    def get_findings(self, path: Path) -> Optional[list]:
        """Get cached findings for a file."""
        if not self.enabled:
            return None

        try:
            rel_path = str(path.relative_to(self.project_root))
        except ValueError:
            rel_path = str(path)

        cached = self._cache.get(rel_path)
        if cached and cached.get("hash") == get_file_hash(path):
            return cached.get("findings", [])
        return None

    def set_findings(self, path: Path, findings: list) -> None:
        """Cache findings for a file."""
        if not self.enabled:
            return

        try:
            rel_path = str(path.relative_to(self.project_root))
        except ValueError:
            rel_path = str(path)

        self._cache[rel_path] = {
            "hash": get_file_hash(path),
            "findings": [
                {
                    "line": f.line,
                    "column": f.column,
                    "rule_id": f.rule_id,
                    "rule_name": f.rule_name,
                    "message": f.message,
                    "severity": f.severity.value,
                    "code": f.code,
                    "fix_hint": f.fix_hint,
                    "why": f.why,
                }
                for f in findings
            ],
        }
        self._dirty = True

    def save(self) -> None:
        """Save cache to disk."""
        self._save()

    def clear(self) -> None:
        """Clear the cache."""
        self._cache = {}
        self._dirty = True
        if self.cache_file.exists():
            try:
                self.cache_file.unlink()
            except OSError:
                pass


def clear_cache(project_root: Path) -> bool:
    """Clear the cache directory for a project."""
    cache_dir = get_cache_dir(project_root)
    if cache_dir.exists():
        import shutil
        try:
            shutil.rmtree(cache_dir)
            return True
        except OSError:
            return False
    return True
