"""Error Pattern Service — records failures and provides preventive guidance."""

from __future__ import annotations

from datetime import UTC, datetime

from cleveragents.domain.models.core.error_pattern import (
    ErrorPattern,
    PreventiveGuidance,
)
from cleveragents.infrastructure.database.error_pattern_repository import (
    ErrorPatternRepository,
)


class ErrorPatternService:
    """Service layer for the Error Pattern Database.

    Implements Layer 4: Predictive Error Prevention.
    """

    def __init__(self, repository: ErrorPatternRepository | None = None) -> None:
        self._repo = repository or ErrorPatternRepository()

    def record_failure(
        self,
        pattern_text: str,
        failure_description: str,
        preventive_checks: tuple[str, ...] = (),
        keywords: tuple[str, ...] = (),
    ) -> ErrorPattern:
        """Record a new failure or update frequency of an existing pattern."""
        # Check if similar pattern already exists
        existing = self._find_existing_pattern(pattern_text)
        if existing is not None:
            # Merge preventive checks (accumulate, don't replace)
            merged_checks = list(existing.preventive_checks)
            for check in preventive_checks:
                if check not in merged_checks:
                    merged_checks.append(check)
            updated = ErrorPattern(
                pattern_id=existing.pattern_id,
                pattern=existing.pattern,
                historical_failures=(
                    *existing.historical_failures,
                    failure_description,
                ),
                preventive_checks=tuple(merged_checks),
                frequency=existing.frequency + 1,
                created_at=existing.created_at,
                last_seen=datetime.now(UTC),
                keywords=keywords if keywords else existing.keywords,
            )
            return self._repo.update(updated)

        new_pattern = ErrorPattern(
            pattern=pattern_text,
            historical_failures=(failure_description,),
            preventive_checks=preventive_checks,
            keywords=keywords or self._extract_keywords(pattern_text),
        )
        return self._repo.create(new_pattern)

    def match_patterns(self, context_text: str) -> PreventiveGuidance:
        """Match context against known error patterns.

        Returns preventive guidance for matched patterns.
        """
        matched = self._repo.match_context(context_text)
        if not matched:
            return PreventiveGuidance()

        all_checks: list[str] = []
        seen: set[str] = set()
        for pattern in matched:
            for check in pattern.preventive_checks:
                if check not in seen:
                    all_checks.append(check)
                    seen.add(check)

        return PreventiveGuidance(
            matched_patterns=tuple(matched),
            preventive_checks=tuple(all_checks),
        )

    def get_statistics(self) -> dict[str, int | float]:
        """Return error pattern database statistics."""
        return self._repo.statistics()

    def list_patterns(self) -> list[ErrorPattern]:
        """List all error patterns, most recent first."""
        return self._repo.list_all()

    def _find_existing_pattern(self, pattern_text: str) -> ErrorPattern | None:
        """Find an existing pattern by exact text match."""
        pattern_lower = pattern_text.lower()
        for p in self._repo.list_all():
            if p.pattern.lower() == pattern_lower:
                return p
        return None

    def _extract_keywords(self, text: str) -> tuple[str, ...]:
        """Extract significant keywords from pattern text for matching."""
        stop_words = frozenset(
            {
                "a",
                "an",
                "the",
                "in",
                "on",
                "at",
                "to",
                "for",
                "of",
                "with",
                "and",
                "or",
                "but",
                "is",
                "was",
                "are",
                "were",
                "be",
                "been",
                "has",
                "had",
                "have",
                "do",
                "does",
                "did",
                "will",
                "would",
                "could",
                "should",
                "may",
                "might",
                "can",
                "this",
                "that",
                "it",
                "its",
                "not",
                "no",
                "from",
                "by",
                "as",
            }
        )
        words = text.lower().split()
        keywords = [w for w in words if len(w) > 2 and w not in stop_words]
        return tuple(keywords[:10])  # Limit to 10 keywords
