"""Intelligence layer: relevance scoring, freshness decay, conflict resolution."""

import math
from datetime import UTC, datetime

from sulci_core.config import SulciConfig
from sulci_core.models import ContextQueryResult, KnowledgeAtom, VerificationStatus
from sulci_core.store import KnowledgeStore


class IntelligenceEngine:
    """Score, rank, and manage knowledge relevance."""

    def __init__(self, config: SulciConfig, store: KnowledgeStore) -> None:
        self.config = config
        self.store = store

    async def query_relevant(
        self,
        query: str,
        limit: int = 10,
        project: str | None = None,
    ) -> list[ContextQueryResult]:
        """Find and rank the most relevant knowledge for a query."""
        # Get candidates from vector search (fetch more than needed for re-ranking)
        candidates = await self.store.search_atoms(query, limit=limit * 3, project=project)

        # Re-score with full relevance formula
        for result in candidates:
            result.relevance_score = self._calculate_relevance(result.atom, result.vector_similarity)

        # Sort by composite relevance
        candidates.sort(key=lambda r: r.relevance_score, reverse=True)

        # Take top N and update access tracking
        top_results = candidates[:limit]
        for result in top_results:
            await self.store.touch_atom(result.atom.id)

        return top_results

    def _calculate_relevance(
        self,
        atom: KnowledgeAtom,
        vector_similarity: float,
    ) -> float:
        """Calculate composite relevance score.

        Formula: 0.5 * vector_similarity + 0.2 * freshness + 0.15 * confidence + 0.15 * frequency
        Applies penalty for unverified/expired atoms.
        """
        freshness = self._freshness_score(atom)
        frequency = self._frequency_score(atom)

        effective_confidence = atom.confidence
        if self._needs_verification(atom):
            effective_confidence = max(0.0, effective_confidence - self.config.unverified_confidence_penalty)

        score = (
            self.config.relevance_vector_weight * vector_similarity
            + self.config.relevance_freshness_weight * freshness
            + self.config.relevance_confidence_weight * effective_confidence
            + self.config.relevance_frequency_weight * frequency
        )

        return min(max(score, 0.0), 1.0)

    def _needs_verification(self, atom: KnowledgeAtom) -> bool:
        """Check if atom is unverified or has an expired verification."""
        if atom.verification_status in (VerificationStatus.UNVERIFIED, VerificationStatus.EXPIRED):
            return True
        if atom.verification_status == VerificationStatus.VERIFIED and atom.verified_at:
            now = datetime.now(UTC)
            verified = atom.verified_at
            if verified.tzinfo is None:
                verified = verified.replace(tzinfo=UTC)
            age_days = (now - verified).total_seconds() / 86400.0
            if age_days > self.config.verification_interval_days:
                return True
        return False

    def _freshness_score(self, atom: KnowledgeAtom) -> float:
        """Exponential decay based on age. Half-life default: 30 days."""
        now = datetime.now(UTC)
        updated = atom.updated_at
        # Normalize timezone — SQLite returns naive datetimes
        if updated.tzinfo is None:
            updated = updated.replace(tzinfo=UTC)
        age_days = (now - updated).total_seconds() / 86400.0
        half_life = self.config.freshness_half_life_days
        return math.exp(-0.693 * age_days / half_life)

    def _frequency_score(self, atom: KnowledgeAtom) -> float:
        """Normalized access frequency. Logarithmic scaling to prevent dominance."""
        if atom.access_count <= 0:
            return 0.0
        # log(1 + count) / log(1 + 100) caps the contribution
        return min(math.log(1 + atom.access_count) / math.log(101), 1.0)

    async def resolve_conflicts(
        self,
        results: list[ContextQueryResult],
    ) -> list[ContextQueryResult]:
        """Resolve conflicts: same subject+predicate with different objects.

        Strategy: newer wins, but mark older ones with reduced confidence.
        """
        seen: dict[tuple, ContextQueryResult] = {}
        resolved = []

        for result in results:
            atom = result.atom
            if atom.subject and atom.predicate:
                key = (atom.subject.lower(), atom.predicate.lower())
                if key in seen:
                    existing = seen[key]
                    if atom.updated_at > existing.atom.updated_at:
                        # New atom is newer — replace
                        resolved.remove(existing)
                        seen[key] = result
                        resolved.append(result)
                    # else: keep existing, skip this one
                else:
                    seen[key] = result
                    resolved.append(result)
            else:
                resolved.append(result)

        return resolved

    def format_context_block(self, results: list[ContextQueryResult]) -> str:
        """Format knowledge atoms into an injection-ready text block."""
        if not results:
            return ""

        lines = ["[Sulci Context — relevant knowledge from your previous interactions]"]
        for result in results:
            atom = result.atom
            confidence_label = "high" if atom.confidence >= 0.8 else "medium" if atom.confidence >= 0.5 else "low"
            line = f"- [{atom.atom_type.value}] {atom.content}"
            if atom.source_tool:
                line += f" (from {atom.source_tool}, {confidence_label} confidence)"
            lines.append(line)
        lines.append("[End Sulci Context]")

        return "\n".join(lines)
