"""Phone number normalization and helper utilities."""

import re

from teams_phone.exceptions import ValidationError


def normalize_phone_number(number: str) -> str:
    """Normalize a phone number by stripping non-digit characters.

    Accepts various formats and converts to digits-only format.
    Validates that the result has at least 10 digits.

    Args:
        number: Phone number in any format (e.g., "+1 (206) 555-1234",
            "1-206-555-1234", "+12065551234").

    Returns:
        Digits-only string (e.g., "12065551234").

    Raises:
        ValidationError: If the number has fewer than 10 digits after
            stripping non-digit characters.

    Examples:
        >>> normalize_phone_number("+1 (206) 555-1234")
        '12065551234'
        >>> normalize_phone_number("1-206-555-1234")
        '12065551234'
        >>> normalize_phone_number("+12065551234")
        '12065551234'
    """
    digits = re.sub(r"\D", "", number)

    if len(digits) < 10:
        raise ValidationError(
            f"Invalid phone number: '{number}' has fewer than 10 digits",
            remediation="Provide a valid phone number with at least 10 digits.",
        )

    return digits


def format_for_assign(number: str) -> str:
    """Format a phone number for the assignNumber/unassignNumber API endpoints.

    These endpoints require digits only (no + prefix).

    Args:
        number: Phone number in any format.

    Returns:
        Digits-only string suitable for Graph API assign/unassign operations.

    Raises:
        ValidationError: If the number is invalid.

    Examples:
        >>> format_for_assign("+1 (206) 555-1234")
        '12065551234'
        >>> format_for_assign("12065551234")
        '12065551234'
    """
    return normalize_phone_number(number)


def format_for_update(number: str) -> str:
    """Format a phone number for the updateNumber API endpoint.

    This endpoint requires E.164 format with + prefix.

    Args:
        number: Phone number in any format.

    Returns:
        E.164 formatted string with + prefix (e.g., "+12065551234").

    Raises:
        ValidationError: If the number is invalid.

    Examples:
        >>> format_for_update("1 (206) 555-1234")
        '+12065551234'
        >>> format_for_update("+12065551234")
        '+12065551234'
    """
    digits = normalize_phone_number(number)
    return f"+{digits}"


def format_for_display(number: str) -> str:
    """Format a phone number for user-friendly display.

    Formats US/Canada NANP numbers as: +1 (NPA) NXX-XXXX
    For international numbers, returns E.164 format.

    Args:
        number: Phone number in any format.

    Returns:
        Formatted string for display (e.g., "+1 (206) 555-1234").

    Raises:
        ValidationError: If the number is invalid.

    Examples:
        >>> format_for_display("12065551234")
        '+1 (206) 555-1234'
        >>> format_for_display("+442071234567")
        '+442071234567'
    """
    digits = normalize_phone_number(number)

    # US/Canada NANP: 11 digits starting with 1
    if len(digits) == 11 and digits.startswith("1"):
        return f"+1 ({digits[1:4]}) {digits[4:7]}-{digits[7:11]}"

    # US/Canada NANP without country code: 10 digits
    if len(digits) == 10:
        return f"+1 ({digits[0:3]}) {digits[3:6]}-{digits[6:10]}"

    # International numbers: return E.164 format
    return f"+{digits}"


def match_pattern(number: str, pattern: str) -> bool:
    """Check if a phone number matches a search pattern.

    Supports wildcards for flexible searching:
    - `*` matches any sequence of characters
    - Exact digit sequences match those digits anywhere in the number
    - Patterns are matched against the normalized (digits-only) number

    Args:
        number: Phone number to check (any format accepted).
        pattern: Search pattern, optionally with wildcards.

    Returns:
        True if the number matches the pattern, False otherwise.

    Raises:
        ValidationError: If the number is invalid.

    Examples:
        >>> match_pattern("+12065551234", "206*")
        True
        >>> match_pattern("+12065551234", "*555*")
        True
        >>> match_pattern("+12065551234", "1234")
        True
        >>> match_pattern("+12065551234", "999")
        False
    """
    digits = normalize_phone_number(number)

    # Normalize pattern: strip non-digit except *
    normalized_pattern = re.sub(r"[^\d*]", "", pattern)

    if not normalized_pattern:
        return False

    # If pattern has no wildcards, treat as substring search
    if "*" not in normalized_pattern:
        return normalized_pattern in digits

    # Convert wildcards to regex pattern
    # Escape any regex special chars first, then replace \* with .*
    regex_pattern = re.escape(normalized_pattern).replace(r"\*", ".*")

    # Use search to find pattern anywhere in the number
    # This allows "206*" to match area codes even with country code prefix
    return bool(re.search(regex_pattern, digits))
