# preprocessing/core/pre_blocking.py
import pandas as pd
import logging
import re
from urllib.parse import urlsplit
from typing import Tuple

log = logging.getLogger(__name__)


class BlockingKeyGenerator:
    """Generates blocking keys for record linkage"""

    DOMAIN_BLOCK_TOKENS = ("domain", "website", "url", "email", "web")
    _NON_ALNUM_RE = re.compile(r"[^a-z0-9]")
    _WWW_PREFIX_RE = re.compile(r"^www\d*\.", re.IGNORECASE)
    _NIL_LITERALS = {"", "nan", "none", "null"}

    @classmethod
    def _is_domain_like_column(cls, block_col: str) -> bool:
        lowered = str(block_col or "").lower()
        return any(token in lowered for token in cls.DOMAIN_BLOCK_TOKENS)

    @classmethod
    def _extract_domain_value(cls, raw_value) -> str:
        if raw_value is None:
            return ""

        text = str(raw_value).strip().lower()
        if text in cls._NIL_LITERALS:
            return ""

        # Email -> domain
        if "@" in text:
            text = text.rsplit("@", 1)[-1]

        # URL-like values
        if "://" in text:
            try:
                parsed = urlsplit(text)
                host = parsed.hostname or parsed.netloc or parsed.path
            except Exception:
                host = text
        else:
            host = text

        host = str(host).strip().lower()
        if "/" in host:
            host = host.split("/", 1)[0]
        if "?" in host:
            host = host.split("?", 1)[0]
        if "#" in host:
            host = host.split("#", 1)[0]
        if ":" in host:
            host = host.split(":", 1)[0]
        host = cls._WWW_PREFIX_RE.sub("", host).rstrip(".")

        if not host or "." not in host:
            return ""

        # Prefer eTLD+1 when tldextract is available.
        try:
            import tldextract

            extracted = tldextract.extract(host)
            if extracted.domain and extracted.suffix:
                host = f"{extracted.domain}.{extracted.suffix}"
            elif extracted.domain:
                host = extracted.domain
        except Exception:
            pass

        return host

    def generate_blocking_keys(
        self, df: pd.DataFrame, block_col: str, key_length: int
    ) -> Tuple[pd.DataFrame, dict]:
        """Generate blocking keys from specified column"""
        result_df = df.copy()

        if block_col in df.columns:
            source_series = df[block_col]
            if self._is_domain_like_column(block_col):
                normalized = source_series.map(self._extract_domain_value)
            else:
                normalized = source_series.astype(str).str.lower()

            # CRITICAL: Use the exact column name the engine expects
            result_df["_active_block_key_"] = (  # Changed to lowercase
                normalized
                .fillna("")
                .str.replace(r"[^a-z0-9]", "", regex=True)
                .str[:key_length]
                .fillna("")  # Handle any NaN values
            )

            # Log statistics
            unique_keys = result_df["_active_block_key_"].nunique()
            empty_keys = (result_df["_active_block_key_"] == "").sum()

            log.info(
                f"Generating blocking keys from column '{block_col}' with length {key_length}"
            )
            log.info(f"Created {unique_keys} unique blocks, {empty_keys} empty")

        else:
            log.warning(f"Blocking column '{block_col}' not found")
            result_df["_active_block_key_"] = ""

        return result_df, {}
