"""
Base constraint checker for inheritance-based constraint checking system.

This module provides the base interface and common functionality for
constraint checking implementations.
"""

from abc import ABC, abstractmethod
from dataclasses import dataclass
from typing import Dict, List, Optional, Tuple

from langchain_core.language_models import BaseChatModel
from loguru import logger

from ..candidates.base_candidate import Candidate
from ..constraints.base_constraint import Constraint


@dataclass
class ConstraintCheckResult:
    """Result of checking a candidate against all constraints."""

    candidate: Candidate
    total_score: float
    constraint_scores: Dict[str, Dict]
    should_reject: bool
    rejection_reason: Optional[str]
    detailed_results: List[Dict]


class BaseConstraintChecker(ABC):
    """
    Base class for constraint checking implementations.

    This provides the common interface and shared functionality that
    all constraint checkers should implement.
    """

    def __init__(
        self,
        model: BaseChatModel,
        evidence_gatherer=None,  # Will be passed in from strategy
        **kwargs,
    ):
        """
        Initialize the base constraint checker.

        Args:
            model: Language model for evidence analysis
            evidence_gatherer: Function to gather evidence (from strategy)
            **kwargs: Additional parameters for specific implementations
        """
        self.model = model
        self.evidence_gatherer = evidence_gatherer

    @abstractmethod
    def check_candidate(
        self, candidate: Candidate, constraints: List[Constraint]
    ) -> ConstraintCheckResult:
        """
        Check a candidate against all constraints.

        Args:
            candidate: The candidate to check
            constraints: List of constraints to check against

        Returns:
            ConstraintCheckResult: Complete evaluation result
        """
        pass

    @abstractmethod
    def should_reject_candidate(
        self, candidate: Candidate, constraint: Constraint, evidence_data: any
    ) -> Tuple[bool, str]:
        """
        Determine if a candidate should be rejected for a specific constraint.

        Args:
            candidate: The candidate being evaluated
            constraint: The constraint being checked
            evidence_data: Evidence data (format depends on implementation)

        Returns:
            Tuple[bool, str]: (should_reject, reason)
        """
        pass

    def _gather_evidence_for_constraint(
        self, candidate: Candidate, constraint: Constraint
    ) -> List[Dict]:
        """Gather evidence for a constraint using the provided evidence gatherer."""
        if self.evidence_gatherer:
            return self.evidence_gatherer(candidate, constraint)
        else:
            logger.warning(
                "No evidence gatherer provided - cannot gather evidence"
            )
            return []

    def _log_constraint_result(
        self,
        candidate: Candidate,
        constraint: Constraint,
        score: float,
        details: Dict,
    ):
        """Log constraint evaluation result."""
        symbol = "✓" if score >= 0.8 else "○" if score >= 0.5 else "✗"
        logger.info(
            f"{symbol} {candidate.name} | {constraint.value}: {int(score * 100)}%"
        )

    def _calculate_weighted_score(
        self, constraint_scores: List[float], weights: List[float]
    ) -> float:
        """Calculate weighted average score."""
        if not constraint_scores or not weights:
            return 0.0
        return sum(
            s * w for s, w in zip(constraint_scores, weights, strict=False)
        ) / sum(weights)
