"""
Shared normalization utilities for matching services.
Eliminates duplicate code between L2A and dedupe services.
"""

import pandas as pd
from typing import Dict, Any


def normalize_score_column(df: pd.DataFrame, inplace: bool = True) -> pd.DataFrame:
    """
    Normalize various score column names to 'score'.
    Handles both 0-1 and 0-100 ranges automatically.

    Args:
        df: DataFrame with score columns
        inplace: Whether to modify in place or return copy

    Returns:
        DataFrame with normalized 'score' column
    """
    if not inplace:
        df = df.copy()

    # Priority order - check most common names first
    score_columns = [
        "confidence_score",
        "match_score",
        "similarity",
        "sim_score",
        "combined_score",
        "overall_score",
        "final_score",
        "weighted_score",
        "ensemble_score",
        "total_score",
    ]

    # If score column already exists and is valid, just normalize range
    if "score" in df.columns:
        # Ensure 0-1 range
        if df["score"].max() > 1.1:
            df["score"] = df["score"] / 100.0
        return df

    # Find first available score column
    for col in score_columns:
        if col in df.columns:
            df["score"] = df[col]

            # Normalize to 0-1 range if needed
            if df["score"].max() > 1.1:
                df["score"] = df["score"] / 100.0

            # Optional: drop the original column to avoid confusion
            # df.drop(columns=[col], inplace=True)
            break

    # If no score column found, check for any column with 'score' in name
    if "score" not in df.columns:
        for col in df.columns:
            if "score" in col.lower():
                df["score"] = df[col]
                if df["score"].max() > 1.1:
                    df["score"] = df["score"] / 100.0
                break

    return df


def normalize_id_columns(
    df: pd.DataFrame, mode: str = "l2a", inplace: bool = True
) -> pd.DataFrame:
    """
    Normalize ID column names based on mode.

    Args:
        df: DataFrame with ID columns
        mode: 'l2a' for lead-to-account, 'dedupe' for deduplication
        inplace: Whether to modify in place or return copy

    Returns:
        DataFrame with normalized ID columns
    """
    if not inplace:
        df = df.copy()

    if mode == "l2a":
        # L2A's comprehensive approach - map to lead_id and account_id
        lead_variants = [
            "lead_id",
            "l_id",
            "s_id",
            "source_id",
            "Id_1",
            "id1",
            "Id_lead",
            "source",
            "src_id",
        ]

        account_variants = [
            "account_id",
            "a_id",
            "r_id",
            "target_id",
            "reference_id",
            "Id_2",
            "id2",
            "Id_acct",
            "target",
            "ref_id",
            "dst_id",
        ]

        # Check for lead ID
        if "lead_id" not in df.columns:
            for col in lead_variants:
                if col in df.columns:
                    df["lead_id"] = df[col]
                    break

        # Check for account ID
        if "account_id" not in df.columns:
            for col in account_variants:
                if col in df.columns:
                    df["account_id"] = df[col]
                    break

    elif mode == "dedupe":
        # Dedupe's approach - map to rec_id_1 and rec_id_2
        id1_variants = [
            "rec_id_1",
            "Id_1",
            "id1",
            "left_id",
            "source_id",
            "s_id",
            "record_1",
        ]

        id2_variants = [
            "rec_id_2",
            "Id_2",
            "id2",
            "right_id",
            "target_id",
            "r_id",
            "record_2",
        ]

        # Check for first record ID
        if "rec_id_1" not in df.columns:
            for col in id1_variants:
                if col in df.columns:
                    df["rec_id_1"] = df[col]
                    break

        # Check for second record ID
        if "rec_id_2" not in df.columns:
            for col in id2_variants:
                if col in df.columns:
                    df["rec_id_2"] = df[col]
                    break

    return df


def extract_id_from_dict(row: Dict[str, Any], mode: str = "l2a") -> tuple:
    """
    Extract IDs from a dictionary row with various possible key names.

    Args:
        row: Dictionary containing ID fields
        mode: 'l2a' or 'dedupe' to determine which IDs to extract

    Returns:
        Tuple of (id1, id2) - either (lead_id, account_id) or (rec_id_1, rec_id_2)
    """
    if mode == "l2a":
        # Try to find lead ID
        lead_id = (
            row.get("lead_id")
            or row.get("l_id")
            or row.get("s_id")
            or row.get("source_id")
            or row.get("Id_1")
            or row.get("id1")
            or row.get("source")
        )

        # Try to find account ID
        account_id = (
            row.get("account_id")
            or row.get("a_id")
            or row.get("r_id")
            or row.get("target_id")
            or row.get("reference_id")
            or row.get("Id_2")
            or row.get("id2")
            or row.get("target")
        )

        return lead_id, account_id

    else:  # dedupe mode
        # Try to find first record ID
        rec_id_1 = (
            row.get("rec_id_1")
            or row.get("Id_1")
            or row.get("id1")
            or row.get("left_id")
            or row.get("source_id")
            or row.get("s_id")
        )

        # Try to find second record ID
        rec_id_2 = (
            row.get("rec_id_2")
            or row.get("Id_2")
            or row.get("id2")
            or row.get("right_id")
            or row.get("target_id")
            or row.get("r_id")
        )

        return rec_id_1, rec_id_2


def normalize_tier_name(tier: str) -> str:
    """
    Normalize confidence tier names to standard values.

    Args:
        tier: Raw tier name

    Returns:
        Normalized tier name: 'CERTAIN', 'LIKELY', or 'POSSIBLE'
    """
    if not tier:
        return "POSSIBLE"

    tier_upper = str(tier).upper()

    # Map various names to standard tiers
    if tier_upper in ["CERTAIN", "HIGH", "EXCELLENT", "PERFECT"]:
        return "CERTAIN"
    elif tier_upper in ["LIKELY", "GOOD", "MEDIUM", "PROBABLE"]:
        return "LIKELY"
    elif tier_upper in ["POSSIBLE", "LOW", "WEAK", "MAYBE"]:
        return "POSSIBLE"
    else:
        # Default based on numeric values if present
        try:
            # Extract number from strings like "TIER_90" or "95"
            import re

            numbers = re.findall(r"\d+", tier)
            if numbers:
                score = int(numbers[0])
                if score >= 90:
                    return "CERTAIN"
                elif score >= 75:
                    return "LIKELY"
                else:
                    return "POSSIBLE"
        except:
            pass

        return "POSSIBLE"  # Default


def normalize_blocking_config(blocking: dict) -> dict:
    """
    Normalize blocking configuration from various formats.

    Args:
        blocking: Raw blocking configuration

    Returns:
        Normalized blocking config with standard keys
    """
    if not blocking:
        return {"apply_blocking": False, "keys": []}

    normalized = {"apply_blocking": False, "keys": []}

    # Check various enable flags
    if (
        blocking.get("apply_blocking")
        or blocking.get("enabled")
        or blocking.get("apply")
        or blocking.get("use_blocking")
    ):
        normalized["apply_blocking"] = True

    # Extract blocking keys
    if "keys" in blocking:
        normalized["keys"] = blocking["keys"]
    elif "blocking_keys" in blocking:
        normalized["keys"] = blocking["blocking_keys"]
    elif "column" in blocking or "source_column" in blocking:
        # Single column format
        col = blocking.get("source_column") or blocking.get("column")
        if col:
            normalized["keys"] = [
                {
                    "source_column": col,
                    "key_length": blocking.get("key_length", 3),
                    "variant": blocking.get("variant", "alnum"),
                }
            ]

    return normalized
