"""Normalize phone numbers to E.164 format with extension preservation."""

from typing import Dict, Any, Optional
import re

__transform_id__ = "normalize_phone_to_e164"
__version__ = "1.2.0"
__updated__ = "2024-09-20"

# Structured error codes
ERROR_CODES = {
    "EMPTY_INPUT": "No phone number provided",
    "NO_DIGITS": "Phone number contains no digits",
    "UNSUPPORTED_COUNTRY": "Country not in supported list",
    "INVALID_FORMAT": "Phone number format not recognized",
    "TOO_SHORT": "Phone number has too few digits",
    "TOO_LONG": "Phone number exceeds E.164 maximum length",
    "PARSE_ERROR": "Unable to parse phone number",
    "INVALID_COUNTRY_CODE": "Invalid country calling code",
}

# Extension patterns
EXT_PATTERNS = [
    r"\b(?:ext\.?|extension|x|;ext=)\s*(\d{1,6})\b",
    r"#\s*(\d{1,6})",
    r",(\d{1,6})$",  # Comma followed by digits at end
]

# Compile patterns
EXT_RE = re.compile("|".join(EXT_PATTERNS), re.IGNORECASE)

# Optional dependency
try:
    import phonenumbers

    HAS_PHONENUMBERS = True
except ImportError:
    HAS_PHONENUMBERS = False


# Country codes with their digit lengths and common formats
COUNTRY_CONFIGS = {
    "US": {"code": "1", "length": 10, "name": "United States"},
    "CA": {"code": "1", "length": 10, "name": "Canada"},
    "GB": {"code": "44", "length": 10, "name": "United Kingdom"},
    "UK": {"code": "44", "length": 10, "name": "United Kingdom"},
    "AU": {"code": "61", "length": 9, "name": "Australia"},
    "DE": {"code": "49", "length": 10, "name": "Germany"},
    "FR": {"code": "33", "length": 9, "name": "France"},
    "JP": {"code": "81", "length": 10, "name": "Japan"},
    "CN": {"code": "86", "length": 11, "name": "China"},
    "IN": {"code": "91", "length": 10, "name": "India"},
    "BR": {"code": "55", "length": 11, "name": "Brazil"},
    "MX": {"code": "52", "length": 10, "name": "Mexico"},
    "ES": {"code": "34", "length": 9, "name": "Spain"},
    "IT": {"code": "39", "length": 10, "name": "Italy"},
    "NL": {"code": "31", "length": 9, "name": "Netherlands"},
    "SE": {"code": "46", "length": 9, "name": "Sweden"},
    "NO": {"code": "47", "length": 8, "name": "Norway"},
    "DK": {"code": "45", "length": 8, "name": "Denmark"},
    "FI": {"code": "358", "length": 9, "name": "Finland"},
    "CH": {"code": "41", "length": 9, "name": "Switzerland"},
    "AT": {"code": "43", "length": 10, "name": "Austria"},
    "BE": {"code": "32", "length": 9, "name": "Belgium"},
    "SG": {"code": "65", "length": 8, "name": "Singapore"},
    "HK": {"code": "852", "length": 8, "name": "Hong Kong"},
    "KR": {"code": "82", "length": 10, "name": "South Korea"},
    "NZ": {"code": "64", "length": 9, "name": "New Zealand"},
    "IL": {"code": "972", "length": 9, "name": "Israel"},
    "AE": {"code": "971", "length": 9, "name": "UAE"},
    "ZA": {"code": "27", "length": 9, "name": "South Africa"},
}


def extract_extension(value: str) -> tuple[str, Optional[str]]:
    """Extract extension from phone number string."""
    if not value:
        return value, None

    match = EXT_RE.search(value)
    if match:
        # Find which group matched
        for i, group in enumerate(match.groups()):
            if group:
                ext = group
                # Remove extension from main number
                core = value[: match.start()] + value[match.end() :]
                return core.strip(), ext

    return value, None


def normalize_phone_to_e164(
    value: str, country: Optional[str] = "US"
) -> Dict[str, Any]:
    """
    Normalize phone number to E.164 format with extension preservation.

    E.164 format:
    - Starts with + and country code
    - No spaces, parentheses, or dashes
    - Maximum 15 digits

    Examples:
        "(415) 555-0100 x456" -> {
            "value": "+14155550100",
            "extension": "456",
            "formatted": "+1 415 555 0100 x456"
        }
        "020 7946 0958" (UK) -> "+442079460958"
        "415-555-0100" -> "+14155550100"
    """
    if not value:
        return {
            "value": "",
            "extension": None,
            "formatted": "",
            "valid": False,
            "error_code": "EMPTY_INPUT",
            "error_message": ERROR_CODES["EMPTY_INPUT"],
            "reason": "empty",  # Keep for backwards compatibility
        }

    # Extract extension
    core_number, extension = extract_extension(value)

    # Try using phonenumbers library if available
    if HAS_PHONENUMBERS:
        try:
            # Parse the core number
            region = (country or "US").upper()
            if region == "UK":
                region = "GB"
            parsed = phonenumbers.parse(core_number, region)

            if phonenumbers.is_valid_number(parsed):
                e164 = phonenumbers.format_number(
                    parsed, phonenumbers.PhoneNumberFormat.E164
                )
                intl = phonenumbers.format_number(
                    parsed, phonenumbers.PhoneNumberFormat.INTERNATIONAL
                )

                # Format with extension if present
                formatted = intl
                if extension:
                    formatted = f"{intl} x{extension}"

                return {
                    "value": e164,
                    "extension": extension,
                    "formatted": formatted,
                    "valid": True,
                    "country": phonenumbers.region_code_for_number(parsed),
                    "format": "E.164",
                }
        except Exception:
            pass  # Fall through to manual parsing

    # Manual fallback parsing
    # Clean the core number
    s = str(core_number).strip()
    has_plus = s.startswith("+")

    # Extract just the digits
    digits = re.sub(r"[^\d]", "", s)

    if not digits:
        return {
            "value": "",
            "extension": extension,
            "formatted": value,
            "valid": False,
            "error_code": "NO_DIGITS",
            "error_message": ERROR_CODES["NO_DIGITS"],
            "reason": "no_digits",  # Keep for backwards compatibility
        }

    # Get country config
    country_upper = (country or "US").upper()

    # Check if country is supported
    if country_upper not in COUNTRY_CONFIGS and country_upper != "US":
        # For explicitly unsupported countries, return error
        if country and country_upper not in ["", "UNKNOWN"]:
            return {
                "value": s,
                "extension": extension,
                "valid": False,
                "error_code": "UNSUPPORTED_COUNTRY",
                "error_message": f"{ERROR_CODES['UNSUPPORTED_COUNTRY']}: {country_upper}",
                "country_assumed": country_upper,
                "formatted": value,
            }

    config = COUNTRY_CONFIGS.get(country_upper, COUNTRY_CONFIGS["US"])

    # Handle different cases
    result = None
    detected_country = None

    # Case 1: Already has country code (with or without +)
    if has_plus or len(digits) > config["length"]:
        # Check if it starts with a known country code
        for cc_key, cc_config in COUNTRY_CONFIGS.items():
            cc = cc_config["code"]
            if digits.startswith(cc):
                remaining = digits[len(cc) :]
                # Flexible length check (allow ±1 digit for variations)
                if abs(len(remaining) - cc_config["length"]) <= 1:
                    result = f"+{digits}"
                    detected_country = cc_key
                    break

        # If no match but has +, keep as is
        if not result and has_plus:
            result = f"+{digits}"
            detected_country = "UNKNOWN"

    # Case 2: Starts with 0 (common for local format in many countries)
    elif digits.startswith("0") and country_upper != "US":
        # Remove leading 0 and add country code
        local_number = digits[1:]
        if abs(len(local_number) - config["length"]) <= 1:
            result = f"+{config['code']}{local_number}"
            detected_country = country_upper

    # Case 3: Appears to be local number
    elif abs(len(digits) - config["length"]) <= 1:
        result = f"+{config['code']}{digits}"
        detected_country = country_upper

    # Case 4: US/CA special handling (1 prefix without +)
    elif country_upper in ["US", "CA"] and digits.startswith("1") and len(digits) == 11:
        result = f"+{digits}"
        detected_country = country_upper

    # Validate the result
    if result:
        # E.164 validation
        validation_error = None
        if not result.startswith("+"):
            validation_error = "INVALID_FORMAT"
            result = None
        elif len(result) > 16:  # +1 for the plus, 15 max digits
            validation_error = "TOO_LONG"
            result = None
        elif len(result) < 8:  # Minimum E.164 length
            validation_error = "TOO_SHORT"
            result = None
        elif not re.match(r"^\+\d{7,15}$", result):
            validation_error = "INVALID_FORMAT"
            result = None

    # Build result with extension
    if result:
        response = {
            "value": result,
            "extension": extension,
            "valid": True,
            "country": detected_country,
            "country_name": COUNTRY_CONFIGS.get(detected_country, {}).get(
                "name", "Unknown"
            ),
            "format": "E.164",
        }

        # Add formatted version for display
        if detected_country == "US" or detected_country == "CA":
            # Format as +1 (415) 555-0100
            if len(result) == 12:  # +1 plus 10 digits
                formatted = f"{result[:2]} ({result[2:5]}) {result[5:8]}-{result[8:]}"
            else:
                formatted = result
        elif detected_country == "GB" or detected_country == "UK":
            # Format as +44 20 7946 0958
            if len(result) == 13:  # +44 plus 10 digits
                formatted = f"{result[:3]} {result[3:5]} {result[5:9]} {result[9:]}"
            else:
                formatted = result
        else:
            formatted = result

        # Add extension to formatted version
        if extension:
            formatted = f"{formatted} x{extension}"

        response["formatted"] = formatted
        return response
    else:
        # Determine specific error
        error_code = (
            validation_error
            if "validation_error" in locals() and validation_error
            else "INVALID_FORMAT"
        )

        # Check if it's an unsupported country
        if detected_country == "UNKNOWN" or (
            country_upper and country_upper not in COUNTRY_CONFIGS
        ):
            error_code = "UNSUPPORTED_COUNTRY"

        return {
            "value": s,  # Return original if can't normalize
            "extension": extension,
            "valid": False,
            "error_code": error_code,
            "error_message": ERROR_CODES.get(error_code, "Unknown error"),
            "reason": "cannot_normalize",  # Keep for backwards compatibility
            "original": s,
            "country_assumed": country_upper,
            "country_detected": detected_country
            if "detected_country" in locals()
            else None,
            "formatted": value,  # Return original value with extension
        }
