"""Company name normalization pipeline."""

import logging
import re

from unidecode import unidecode

from corp_names.data.company_suffixes import COMPANY_SUFFIX_SET, COMPANY_SUFFIXES, MULTI_WORD_SUFFIXES
from corp_names.models import NormalizedCompany

log = logging.getLogger(__name__)


def normalize_company(name: str) -> NormalizedCompany:
    """Normalize a company name: strip legal suffixes, detect entity type, lowercase.

    Pipeline:
    1. Strip whitespace and collapse multiple spaces
    2. Strip outer punctuation (trailing periods/commas, quotes)
    3. Normalize unicode (unidecode)
    4. Remove "The" prefix
    5. Normalize "&" → "and"
    6. Normalize dots in suffix-like tokens (N.V. → NV)
    7. Strip legal suffixes and detect entity type
    8. Lowercase the normalized form
    9. Clean up trailing punctuation and whitespace
    """
    original = name

    # 1. Strip whitespace and collapse
    name = re.sub(r"\s+", " ", name).strip()

    if not name:
        return NormalizedCompany(
            original=original, normalized="", suffix="", entity_type="", the_prefix_stripped=False,
        )

    # 2. Strip outer punctuation
    name = name.strip(".,;:'\"")
    name = name.strip()

    # 3. Unicode normalize
    name = unidecode(name)

    # 4. Remove "The" prefix
    the_prefix_stripped = False
    if re.match(r"^The\s+", name, re.IGNORECASE):
        name = re.sub(r"^The\s+", "", name, flags=re.IGNORECASE)
        the_prefix_stripped = True

    # 5. Normalize "&" → "and"
    name = name.replace(" & ", " and ")
    name = re.sub(r"(?<!\w)&(?!\w)", "and", name)

    # 6. Normalize dots — strip dots from tokens (N.V. → NV, Inc. → Inc)
    #    but preserve dots that are part of domain names (amazon.com)
    tokens = _tokenize_preserving_dots(name)
    name = " ".join(tokens)

    # 7. Strip legal suffixes
    suffix, entity_type, name = _strip_suffixes(name)

    # 8. Lowercase
    normalized = name.lower()

    # 9. Clean up — collapse whitespace, strip trailing punctuation
    normalized = re.sub(r"[,;\s]+$", "", normalized)
    normalized = re.sub(r"\s+", " ", normalized).strip()

    return NormalizedCompany(
        original=original,
        normalized=normalized,
        suffix=suffix,
        entity_type=entity_type,
        the_prefix_stripped=the_prefix_stripped,
    )


def _tokenize_preserving_dots(name: str) -> list[str]:
    """Tokenize name, stripping dots from abbreviation-like tokens.

    Keeps dots in tokens that look like domain names (contain dots between
    alphanumeric chars without spaces, e.g. amazon.com).
    Strips dots from abbreviation tokens like N.V., Inc., S.A.
    """
    raw_tokens = re.split(r"[,;\s]+", name)
    result = []
    for token in raw_tokens:
        if not token:
            continue
        # If token looks like an abbreviation (has dots between single chars: N.V., S.A.)
        # or is a simple suffix with trailing dot (Inc., Corp.), strip dots
        stripped = token.replace(".", "")
        if stripped:
            result.append(stripped)
    return result


def _strip_suffixes(name: str) -> tuple[str, str, str]:
    """Detect suffix, return (suffix, entity_type, remaining_name).

    Tries multi-word suffixes first (longest match), then single-word.
    """
    name_lower = name.lower()
    tokens = name_lower.split()

    if not tokens:
        return "", "", name

    # Try multi-word suffixes first (longest first)
    for multi in MULTI_WORD_SUFFIXES:
        multi_words = multi.split()
        n = len(multi_words)
        if len(tokens) > n and tokens[-n:] == multi_words:
            entry = COMPANY_SUFFIXES[multi]
            remaining = " ".join(name.split()[:-n])
            return multi, entry.get("type", ""), remaining

    # Try two-token combined suffix (e.g. "co ltd" from separate tokens)
    if len(tokens) >= 2:
        combined = f"{tokens[-2]} {tokens[-1]}"
        if combined in COMPANY_SUFFIX_SET:
            entry = COMPANY_SUFFIXES[combined]
            remaining = " ".join(name.split()[:-2])
            return combined, entry.get("type", ""), remaining

    # Try single-word suffix (last token)
    last = tokens[-1]
    if last in COMPANY_SUFFIX_SET:
        entry = COMPANY_SUFFIXES[last]
        remaining = " ".join(name.split()[:-1])
        return last, entry.get("type", ""), remaining

    return "", "", name
