"""
Intelligent Utils API - Compliant Version
==========================================
Replaces direct fuzzy library usage with engine-based intelligence.
All scoring and matching goes through the engine client.
"""

from typing import Dict, Any, List, Optional
import logging

from ..services.engine_client import get_engine_client
from ..services.base_intelligent_service import BaseIntelligentService

logger = logging.getLogger(__name__)


class IntelligentUtils:
    """
    Utility functions that use engine intelligence instead of external fuzzy libs.
    This replaces the old utils.py that had direct fuzzy matching.
    """

    def __init__(self):
        self.engine = get_engine_client()
        self._base_service = None

    async def get_base_service(self):
        """Lazy initialization of base service"""
        if self._base_service is None:
            self._base_service = BaseIntelligentService()
        return self._base_service

    async def score_pair(self, str1: str, str2: str, algorithm: str = "auto") -> float:
        """
        Score similarity between two strings using engine.

        Replaces:
        - fuzz.ratio()
        - fuzz.partial_ratio()
        - fuzz.token_sort_ratio()

        Args:
            str1: First string
            str2: Second string
            algorithm: Algorithm to use or "auto" for intelligent selection

        Returns:
            Similarity score between 0 and 1
        """
        if not str1 or not str2:
            return 0.0

        if algorithm == "auto":
            # Determine best algorithm based on string characteristics
            avg_len = (len(str1) + len(str2)) / 2
            has_multiple_words = " " in str1 or " " in str2

            if avg_len < 10:
                algorithm = "JaroWinkler"
            elif has_multiple_words:
                algorithm = "TokenSetRatio"
            else:
                algorithm = "WRatio"

        # Use engine for scoring
        try:
            result = await self.engine.score_pair(
                record1={"text": str1},
                record2={"text": str2},
                mappings=[
                    {
                        "source": "text",
                        "reference": "text",
                        "weight": 1.0,
                        "algorithm": algorithm,
                    }
                ],
            )
            return result / 100.0  # Convert to 0-1 scale
        except Exception as e:
            logger.warning(f"Engine scoring failed: {e}, returning 0")
            return 0.0

    async def suggest_column_mappings(
        self,
        left_columns: List[str],
        right_columns: List[str],
        sample_data: Optional[Dict] = None,
    ) -> List[Dict[str, Any]]:
        """
        Suggest column mappings using intelligence API.

        Replaces the old fuzzy column matching in utils.py.

        Args:
            left_columns: Source column names
            right_columns: Target column names
            sample_data: Optional sample data for better matching

        Returns:
            List of mapping suggestions with scores
        """
        mappings = []
        mapped_right_cols = set()

        for left_col in left_columns:
            best_match = None
            best_score = 0.0

            for right_col in right_columns:
                if right_col in mapped_right_cols:
                    continue

                # Calculate multiple similarity scores
                scores = []

                # Exact match
                if left_col.lower() == right_col.lower():
                    scores.append(1.0)

                # Use engine for fuzzy matching
                fuzzy_score = await self.score_pair(left_col, right_col, "WRatio")
                scores.append(fuzzy_score * 0.8)

                # Partial match
                partial_score = await self.score_pair(
                    left_col, right_col, "PartialRatio"
                )
                scores.append(partial_score * 0.7)

                # Token-based match
                token_score = await self.score_pair(
                    left_col, right_col, "TokenSetRatio"
                )
                scores.append(token_score * 0.9)

                # Check for common abbreviations
                if self._is_abbreviation(left_col, right_col):
                    scores.append(0.85)

                # Semantic similarity based on common patterns
                if self._is_semantic_match(left_col, right_col):
                    scores.append(0.9)

                # Take the maximum score
                final_score = max(scores) if scores else 0.0

                if final_score > best_score:
                    best_score = final_score
                    best_match = right_col

            # Only suggest mappings above threshold
            if best_match and best_score > 0.5:
                mappings.append(
                    {
                        "source": left_col,
                        "target": best_match,
                        "score": best_score,
                        "confidence": self._score_to_confidence(best_score),
                    }
                )
                mapped_right_cols.add(best_match)

        return sorted(mappings, key=lambda x: x["score"], reverse=True)

    def _is_abbreviation(self, str1: str, str2: str) -> bool:
        """Check if strings are abbreviations of each other"""
        s1_lower = str1.lower().replace("_", "").replace("-", "")
        s2_lower = str2.lower().replace("_", "").replace("-", "")

        # Check common abbreviations
        abbrev_map = {
            "id": "identifier",
            "num": "number",
            "addr": "address",
            "tel": "telephone",
            "phone": "telephone",
            "email": "email_address",
            "co": "company",
            "org": "organization",
            "dept": "department",
            "mgr": "manager",
            "desc": "description",
            "qty": "quantity",
            "amt": "amount",
            "cust": "customer",
            "prod": "product",
        }

        for abbr, full in abbrev_map.items():
            if (abbr in s1_lower and full in s2_lower) or (
                abbr in s2_lower and full in s1_lower
            ):
                return True

        return False

    def _is_semantic_match(self, str1: str, str2: str) -> bool:
        """Check if strings are semantically similar"""
        s1_lower = str1.lower()
        s2_lower = str2.lower()

        # Common semantic groups
        semantic_groups = [
            {"name", "title", "label", "description"},
            {"phone", "telephone", "tel", "mobile", "cell"},
            {"email", "email_address", "e_mail", "mail"},
            {"address", "addr", "location", "street"},
            {"city", "town", "municipality"},
            {"state", "province", "region"},
            {"zip", "zipcode", "postal_code", "postcode"},
            {"company", "organization", "org", "business", "firm"},
            {"first_name", "firstname", "given_name", "fname"},
            {"last_name", "lastname", "surname", "family_name", "lname"},
            {"id", "identifier", "key", "code"},
            {"date", "datetime", "timestamp", "time"},
            {"amount", "value", "price", "cost", "total"},
        ]

        for group in semantic_groups:
            words1 = set(s1_lower.replace("_", " ").split())
            words2 = set(s2_lower.replace("_", " ").split())

            if words1.intersection(group) and words2.intersection(group):
                return True

        return False

    def _score_to_confidence(self, score: float) -> str:
        """Convert numeric score to confidence level"""
        if score >= 0.95:
            return "CERTAIN"
        elif score >= 0.85:
            return "HIGH"
        elif score >= 0.70:
            return "MEDIUM"
        elif score >= 0.50:
            return "LOW"
        else:
            return "VERY_LOW"


# Singleton instance
intelligent_utils = IntelligentUtils()


# Compatibility functions for drop-in replacement
async def fuzzy_score(str1: str, str2: str) -> float:
    """Drop-in replacement for old fuzzy_score function"""
    return await intelligent_utils.score_pair(str1, str2, "WRatio")


async def partial_score(str1: str, str2: str) -> float:
    """Drop-in replacement for old partial_score function"""
    return await intelligent_utils.score_pair(str1, str2, "PartialRatio")


async def token_score(str1: str, str2: str) -> float:
    """Drop-in replacement for old token_score function"""
    return await intelligent_utils.score_pair(str1, str2, "TokenSetRatio")


async def suggest_mappings(
    left_columns: List[str],
    right_columns: List[str],
    sample_data: Optional[Dict] = None,
) -> List[Dict[str, Any]]:
    """Drop-in replacement for old suggest_mappings function"""
    return await intelligent_utils.suggest_column_mappings(
        left_columns, right_columns, sample_data
    )
