from __future__ import annotations

import re
from typing import Any, Dict, Optional, Tuple

APPROX_PATTERN = re.compile(r"^(~|approximately)\s*", re.IGNORECASE)
PLUS_PATTERN = re.compile(r"^(\d[\d,]*(?:\.\d+)?[kKmM]?)\+")
NUMBER_TOKEN_PATTERN = re.compile(r"\d[\d,]*(?:\.\d+)?[kKmM]?")
EMPLOYEE_HINT_PATTERN = re.compile(
    r"\b(employee|employees|headcount|people|staff|fte|team members?)\b",
    re.IGNORECASE,
)

_DASH_TRANSLATION = str.maketrans(
    {
        "\u2010": "-",
        "\u2011": "-",
        "\u2012": "-",
        "\u2013": "-",
        "\u2014": "-",
        "\u2015": "-",
        "\u2212": "-",
    }
)


def normalize_range_text(text: str) -> str:
    return str(text or "").translate(_DASH_TRANSLATION).strip()


def parse_number_with_suffix(text: str) -> Optional[int]:
    cleaned = normalize_range_text(text).replace(",", "").replace(" ", "")
    if not cleaned:
        return None

    multiplier = 1
    if cleaned.lower().endswith("k"):
        multiplier = 1000
        cleaned = cleaned[:-1]
    elif cleaned.lower().endswith("m"):
        multiplier = 1_000_000
        cleaned = cleaned[:-1]

    try:
        return int(float(cleaned) * multiplier)
    except ValueError:
        return None


def looks_like_employee_count(value: Any) -> bool:
    if not value:
        return False
    return bool(EMPLOYEE_HINT_PATTERN.search(str(value)))


def parse_employee_count(value: Any) -> Tuple[Optional[int], Optional[str], Dict[str, Any]]:
    if isinstance(value, (int, float)):
        return int(value), None, {}

    text = normalize_range_text(str(value or ""))
    if not text:
        return None, "empty", {}

    suffix_candidate = _try_suffix_token(text)
    if suffix_candidate and "-" not in text:
        return suffix_candidate, None, {}

    plus_match = PLUS_PATTERN.match(text)
    if plus_match:
        parsed = parse_number_with_suffix(plus_match.group(1))
        if parsed is not None:
            return parsed, None, {"plus_suffix": True}

    approx_match = APPROX_PATTERN.match(text)
    if approx_match:
        parsed = _extract_numeric_part(APPROX_PATTERN.sub("", text))
        if parsed is not None:
            return parsed, None, {"approximate": True}

    if "-" in text:
        parsed_range, range_meta = _parse_range(text)
        if parsed_range is None:
            return None, "ambiguous_range", range_meta
        return parsed_range, None, range_meta

    parsed = _extract_numeric_part(text)
    if parsed is not None:
        return parsed, None, {}

    return None, "no_numeric_value", {}


def _parse_range(text: str) -> Tuple[Optional[int], Dict[str, Any]]:
    parts = [part.strip() for part in text.split("-", 1)]
    if len(parts) != 2 or not parts[0] or not parts[1]:
        return None, {"raw": text}

    low_raw, high_raw = parts
    low_suffix = _extract_suffix(low_raw)
    high_suffix = _extract_suffix(high_raw)

    if low_suffix and high_suffix and low_suffix != high_suffix:
        return None, {
            "raw": text,
            "low_raw": low_raw,
            "high_raw": high_raw,
            "low_suffix": low_suffix,
            "high_suffix": high_suffix,
        }

    shared_suffix = low_suffix or high_suffix
    inferred_shared_suffix = False

    if shared_suffix:
        if not low_suffix:
            low_raw = f"{low_raw}{shared_suffix}"
            inferred_shared_suffix = True
        if not high_suffix:
            high_raw = f"{high_raw}{shared_suffix}"
            inferred_shared_suffix = True

    low = _extract_numeric_part(low_raw)
    high = _extract_numeric_part(high_raw)
    if low is None or high is None:
        return None, {"raw": text, "low_raw": low_raw, "high_raw": high_raw}

    if low > high:
        low, high = high, low

    return (low + high) // 2, {
        "range_low": low,
        "range_high": high,
        "shared_suffix_inferred": inferred_shared_suffix,
    }


def _try_suffix_token(text: str) -> Optional[int]:
    token = _extract_token(text)
    if not token or token[-1].lower() not in {"k", "m"}:
        return None
    return parse_number_with_suffix(token)


def _extract_numeric_part(text: str) -> Optional[int]:
    token = _extract_token(text)
    if not token:
        return None
    return parse_number_with_suffix(token)


def _extract_token(text: str) -> str:
    match = NUMBER_TOKEN_PATTERN.search(normalize_range_text(text))
    return match.group(0) if match else ""


def _extract_suffix(text: str) -> str:
    token = _extract_token(text)
    if token and token[-1].isalpha():
        return token[-1].lower()
    return ""
