"""Auto-detection utilities for zero-config matching."""

from __future__ import annotations

from typing import Dict, Any, Optional, List
import pandas as pd

from fmatch.core.engine import auto_select_id_column


async def auto_detect_configuration(
    source_df: pd.DataFrame,
    ref_df: Optional[pd.DataFrame],
    mode: str,
    user_overrides: Dict[str, Any],
) -> Dict[str, Any]:
    """
    Leverages ALL your existing intelligence for zero-config matching.
    """

    # 1. Auto-detect ID columns (use your existing function)
    source_id = (
        user_overrides.get("source_id_column")
        or source_df.attrs.get("suggested_id_column")
        or auto_select_id_column(source_df, file_source="api", enable_telemetry=True)
    )

    reference_id = None
    if mode == "match" and ref_df is not None:
        reference_id = (
            user_overrides.get("reference_id_column")
            or ref_df.attrs.get("suggested_id_column")
            or auto_select_id_column(ref_df, file_source="api", enable_telemetry=True)
        )

    # 2. Auto-detect column mappings
    mappings = []
    if mode == "match" and ref_df is not None:
        # Use AutoMapper if available
        try:
            from fmatch.core.column_mapping import AutoMapper

            auto_mapper = AutoMapper()
            suggested_mappings = auto_mapper.suggest(source_df, ref_df, threshold=75)

            # Optimize algorithms per field (if available)
            mappings = optimize_mapping_algorithms(
                suggested_mappings, source_df, ref_df
            )
        except ImportError:
            # Fallback to simple name matching
            mappings = create_fallback_mappings(
                source_df, ref_df, source_id, reference_id
            )
    else:
        # For dedupe, create identity mappings
        mappings = [
            {"source": col, "weight": 1.0}
            for col in source_df.columns
            if col != source_id and not col.startswith("_")
        ]

    # 3. Determine optimal blocking strategy
    blocking_strategy = {}
    try:
        from fmatch.core.blocking import (
            determine_optimal_blocking_strategy,
            BlockingMode,
        )

        blocking_mode = BlockingMode.DEDUPE if mode == "dedupe" else BlockingMode.MATCH
        blocking_strategy = determine_optimal_blocking_strategy(
            df_src=source_df,
            ref_df=ref_df,
            mappings=mappings,
            mode=blocking_mode,
            ruleset="default",
            weight_profile="balanced",
            algorithm=user_overrides.get("algorithm") or "WRatio",
        )
    except (ImportError, AttributeError):
        # Fallback blocking strategy
        blocking_strategy = create_fallback_blocking_strategy(source_df, ref_df, mode)

    # 4. Select algorithm if not overridden
    algorithm = user_overrides.get("algorithm")
    if not algorithm:
        if blocking_strategy.get("recommended_algorithm"):
            algorithm = blocking_strategy["recommended_algorithm"]
        else:
            # Simple heuristic based on data characteristics
            algorithm = select_algorithm_heuristic(source_df, ref_df, mode)

    # 5. Determine threshold if not overridden
    threshold = user_overrides.get("threshold")
    if threshold is None:
        threshold = 85 if mode == "dedupe" else 80

    return {
        "source_id_column": source_id,
        "reference_id_column": reference_id,
        "mappings": mappings,
        "algorithm": algorithm,
        "threshold": threshold,
        "blocking_column": blocking_strategy.get("source_block_col"),
        "source_blocking_column": blocking_strategy.get("source_block_col"),
        "reference_blocking_column": blocking_strategy.get("ref_block_col"),
        "block_key_length": blocking_strategy.get("block_key_len", 3),
        "preproc_variant": blocking_strategy.get("preproc", "alnum"),
        "enable_multi_algo": any(m.get("preferred_algo") for m in mappings),
        "quality_score": blocking_strategy.get("quality_score", 0),
        "estimated_comparisons": blocking_strategy.get("estimated_comparisons", 0),
        "ruleset_name": "default",
        "preprocessing_rules": [],
        "custom_match_rules": [],
    }


def optimize_mapping_algorithms(
    mappings: List[Dict[str, Any]], source_df: pd.DataFrame, ref_df: pd.DataFrame
) -> List[Dict[str, Any]]:
    """
    Optimize algorithm selection per field based on data characteristics.
    """
    optimized = []

    for mapping in mappings:
        src_col = mapping.get("source")
        ref_col = mapping.get("reference", mapping.get("target"))

        if not src_col or src_col not in source_df.columns:
            continue

        src_name = str(src_col).lower()
        ref_name = str(ref_col).lower() if ref_col else ""
        type_source = (mapping.get("type_source") or "").lower()
        type_ref = (mapping.get("type_reference") or "").lower()
        domain_tokens = {"domain", "website", "url", "profile_url"}
        domain_keywords = ["domain", "website", "url", "site", "web", ".com"]
        is_domain = (
            type_source in domain_tokens
            or type_ref in domain_tokens
            or any(kw in src_name or kw in ref_name for kw in domain_keywords)
        )

        optimized_map = {**mapping}

        if is_domain:
            existing_transforms = list(mapping.get("transforms") or [])
            remaining = [
                t for t in existing_transforms if t not in {"to_domain", "lower"}
            ]
            optimized_map["transforms"] = ["to_domain", "lower"] + remaining
            optimized_map["preferred_algo"] = "Exact"
            optimized_map["weight"] = 1.0
            optimized_map["preprocessing"] = "raw"
            optimized.append(optimized_map)
            continue

        # Analyze field characteristics for non-domain columns
        src_values = source_df[src_col].dropna().astype(str)
        avg_length = src_values.str.len().mean()
        has_spaces = src_values.str.contains(" ").any()

        if avg_length < 10:
            preferred_algo = "WRatio"
        elif has_spaces and avg_length > 30:
            preferred_algo = "TokenSetRatio"
        else:
            preferred_algo = "WRatio"

        optimized_map["preferred_algo"] = preferred_algo
        optimized.append(optimized_map)

    return optimized


def create_fallback_mappings(
    source_df: pd.DataFrame, ref_df: pd.DataFrame, source_id: str, reference_id: str
) -> List[Dict[str, Any]]:
    """
    Create simple name-based mappings when AutoMapper is not available.
    """
    mappings = []
    source_cols = set(source_df.columns) - {source_id}
    ref_cols = set(ref_df.columns) - {reference_id}

    # Exact name matches
    for col in source_cols:
        if col in ref_cols:
            mappings.append(
                {"source": col, "reference": col, "weight": 1.0, "similarity": 100}
            )

    # Similar name matches
    for src_col in source_cols:
        for ref_col in ref_cols:
            if src_col.lower() == ref_col.lower():
                mappings.append(
                    {
                        "source": src_col,
                        "reference": ref_col,
                        "weight": 0.9,
                        "similarity": 90,
                    }
                )

    return mappings


def create_fallback_blocking_strategy(
    source_df: pd.DataFrame, ref_df: Optional[pd.DataFrame], mode: str
) -> Dict[str, Any]:
    """
    Create a simple blocking strategy when advanced blocking is not available.
    """
    # Find the best text column for blocking
    text_cols = source_df.select_dtypes(include=["object"]).columns

    blocking_col = None
    for col in ["name", "company", "title", "address"]:
        if col in text_cols:
            blocking_col = col
            break

    if not blocking_col and len(text_cols) > 0:
        blocking_col = text_cols[0]

    return {
        "source_block_col": blocking_col,
        "ref_block_col": blocking_col if ref_df is not None else None,
        "block_key_len": 3,
        "preproc": "alnum",
        "quality_score": 0.5,
        "estimated_comparisons": len(source_df)
        * (len(ref_df) if ref_df is not None else len(source_df)),
    }


def select_algorithm_heuristic(
    source_df: pd.DataFrame, ref_df: Optional[pd.DataFrame], mode: str
) -> str:
    """
    Select algorithm based on data characteristics.
    """
    # Analyze text columns
    text_cols = source_df.select_dtypes(include=["object"]).columns
    if len(text_cols) == 0:
        return "Levenshtein"

    # Sample some text data
    sample_size = min(100, len(source_df))
    sample_texts = []

    for col in text_cols[:3]:  # Check up to 3 text columns
        sample_texts.extend(
            source_df[col]
            .dropna()
            .astype(str)
            .sample(n=min(sample_size, len(source_df[col].dropna())), random_state=42)
            .tolist()
        )

    if not sample_texts:
        return "WRatio"

    # Calculate average length and word count
    avg_length = sum(len(t) for t in sample_texts) / len(sample_texts)
    avg_words = sum(len(t.split()) for t in sample_texts) / len(sample_texts)

    # Decision logic
    if avg_length < 15:
        return "Levenshtein"
    elif avg_words > 5:
        return "token_set_ratio"
    else:
        return "WRatio"
