"""Unit tests for phone number utility functions."""

import pytest

from teams_phone.constants import ExitCode
from teams_phone.exceptions import ValidationError
from teams_phone.utils import (
    format_for_assign,
    format_for_display,
    format_for_update,
    match_pattern,
    normalize_phone_number,
)


class TestNormalizePhoneNumber:
    """Tests for normalize_phone_number function."""

    @pytest.mark.unit
    def test_e164_format_with_plus(self) -> None:
        """E.164 number with + prefix normalizes to digits only."""
        assert normalize_phone_number("+12065551234") == "12065551234"

    @pytest.mark.unit
    def test_with_spaces(self) -> None:
        """Number with spaces normalizes correctly."""
        assert normalize_phone_number("1 206 555 1234") == "12065551234"

    @pytest.mark.unit
    def test_with_dashes(self) -> None:
        """Number with dashes normalizes correctly."""
        assert normalize_phone_number("1-206-555-1234") == "12065551234"

    @pytest.mark.unit
    def test_with_parentheses(self) -> None:
        """Number with parentheses normalizes correctly."""
        assert normalize_phone_number("+1 (206) 555-1234") == "12065551234"

    @pytest.mark.unit
    def test_mixed_formatting(self) -> None:
        """Number with mixed formatting normalizes correctly."""
        assert normalize_phone_number("+1 (206) 555-1234") == "12065551234"
        assert normalize_phone_number("(206) 555-1234") == "2065551234"

    @pytest.mark.unit
    def test_digits_only_input(self) -> None:
        """Already-normalized number passes through unchanged."""
        assert normalize_phone_number("12065551234") == "12065551234"

    @pytest.mark.unit
    def test_ten_digit_number(self) -> None:
        """Ten-digit number (no country code) normalizes correctly."""
        assert normalize_phone_number("2065551234") == "2065551234"

    @pytest.mark.unit
    def test_international_number(self) -> None:
        """International number normalizes correctly."""
        assert normalize_phone_number("+442071234567") == "442071234567"

    @pytest.mark.unit
    def test_invalid_fewer_than_ten_digits(self) -> None:
        """Number with fewer than 10 digits raises ValidationError."""
        with pytest.raises(ValidationError) as exc_info:
            normalize_phone_number("123456789")
        assert "fewer than 10 digits" in str(exc_info.value)
        assert exc_info.value.exit_code == ExitCode.VALIDATION_ERROR

    @pytest.mark.unit
    def test_invalid_empty_string(self) -> None:
        """Empty string raises ValidationError."""
        with pytest.raises(ValidationError) as exc_info:
            normalize_phone_number("")
        assert exc_info.value.exit_code == ExitCode.VALIDATION_ERROR

    @pytest.mark.unit
    def test_invalid_only_non_digits(self) -> None:
        """String with only non-digits raises ValidationError."""
        with pytest.raises(ValidationError) as exc_info:
            normalize_phone_number("abc-def-ghij")
        assert exc_info.value.exit_code == ExitCode.VALIDATION_ERROR

    @pytest.mark.unit
    def test_validation_error_has_remediation(self) -> None:
        """ValidationError includes remediation guidance."""
        with pytest.raises(ValidationError) as exc_info:
            normalize_phone_number("12345")
        assert exc_info.value.remediation is not None


class TestFormatForAssign:
    """Tests for format_for_assign function (assignNumber/unassignNumber API)."""

    @pytest.mark.unit
    def test_e164_input(self) -> None:
        """E.164 input returns digits only (no + prefix)."""
        assert format_for_assign("+12065551234") == "12065551234"

    @pytest.mark.unit
    def test_formatted_input(self) -> None:
        """Formatted input returns digits only."""
        assert format_for_assign("+1 (206) 555-1234") == "12065551234"

    @pytest.mark.unit
    def test_already_digits_only(self) -> None:
        """Already digits-only input passes through."""
        assert format_for_assign("12065551234") == "12065551234"

    @pytest.mark.unit
    def test_invalid_number_raises(self) -> None:
        """Invalid number raises ValidationError."""
        with pytest.raises(ValidationError):
            format_for_assign("123")


class TestFormatForUpdate:
    """Tests for format_for_update function (updateNumber API)."""

    @pytest.mark.unit
    def test_digits_only_input(self) -> None:
        """Digits-only input gets + prefix."""
        assert format_for_update("12065551234") == "+12065551234"

    @pytest.mark.unit
    def test_e164_input(self) -> None:
        """E.164 input stays normalized with + prefix."""
        assert format_for_update("+12065551234") == "+12065551234"

    @pytest.mark.unit
    def test_formatted_input(self) -> None:
        """Formatted input normalizes with + prefix."""
        assert format_for_update("1 (206) 555-1234") == "+12065551234"

    @pytest.mark.unit
    def test_international_number(self) -> None:
        """International number formats correctly."""
        assert format_for_update("+44 20 7123 4567") == "+442071234567"

    @pytest.mark.unit
    def test_invalid_number_raises(self) -> None:
        """Invalid number raises ValidationError."""
        with pytest.raises(ValidationError):
            format_for_update("123")


class TestFormatForDisplay:
    """Tests for format_for_display function (user-friendly display)."""

    @pytest.mark.unit
    def test_us_number_with_country_code(self) -> None:
        """US number with country code formats as +1 (NPA) NXX-XXXX."""
        assert format_for_display("12065551234") == "+1 (206) 555-1234"

    @pytest.mark.unit
    def test_us_number_e164(self) -> None:
        """US E.164 number formats correctly."""
        assert format_for_display("+12065551234") == "+1 (206) 555-1234"

    @pytest.mark.unit
    def test_us_number_ten_digits(self) -> None:
        """US 10-digit number (no country code) formats correctly."""
        assert format_for_display("2065551234") == "+1 (206) 555-1234"

    @pytest.mark.unit
    def test_formatted_us_input(self) -> None:
        """Formatted US input reformats correctly."""
        assert format_for_display("+1 (206) 555-1234") == "+1 (206) 555-1234"

    @pytest.mark.unit
    def test_international_number(self) -> None:
        """International number returns E.164 format."""
        assert format_for_display("+442071234567") == "+442071234567"

    @pytest.mark.unit
    def test_long_international_number(self) -> None:
        """Long international number returns E.164 format."""
        assert format_for_display("+49301234567890") == "+49301234567890"

    @pytest.mark.unit
    def test_invalid_number_raises(self) -> None:
        """Invalid number raises ValidationError."""
        with pytest.raises(ValidationError):
            format_for_display("123")


class TestMatchPattern:
    """Tests for match_pattern function (wildcard pattern matching)."""

    @pytest.mark.unit
    def test_last_four_digits_match(self) -> None:
        """Pattern matching last four digits."""
        assert match_pattern("+12065551234", "1234") is True
        assert match_pattern("+12065551234", "5678") is False

    @pytest.mark.unit
    def test_area_code_wildcard(self) -> None:
        """Area code with wildcard matches all numbers in that area."""
        assert match_pattern("+12065551234", "206*") is True
        assert match_pattern("+12065551234", "425*") is False

    @pytest.mark.unit
    def test_prefix_wildcard(self) -> None:
        """Wildcard at start matches any prefix."""
        assert match_pattern("+12065551234", "*1234") is True
        assert match_pattern("+12065551234", "*5678") is False

    @pytest.mark.unit
    def test_contains_wildcard(self) -> None:
        """Wildcards on both sides for contains match."""
        assert match_pattern("+12065551234", "*555*") is True
        assert match_pattern("+12065551234", "*999*") is False

    @pytest.mark.unit
    def test_exact_match_with_wildcards(self) -> None:
        """Full pattern with wildcards."""
        assert match_pattern("+12065551234", "1206*1234") is True
        assert match_pattern("+12065551234", "1206*5678") is False

    @pytest.mark.unit
    def test_full_number_no_wildcards(self) -> None:
        """Full number without wildcards uses substring match."""
        assert match_pattern("+12065551234", "12065551234") is True
        assert match_pattern("+12065551234", "2065551234") is True

    @pytest.mark.unit
    def test_partial_match_no_wildcard(self) -> None:
        """Partial pattern without wildcard matches as substring."""
        assert match_pattern("+12065551234", "555") is True
        assert match_pattern("+12065551234", "206") is True
        assert match_pattern("+12065551234", "999") is False

    @pytest.mark.unit
    def test_formatted_pattern(self) -> None:
        """Pattern with formatting is normalized."""
        assert match_pattern("+12065551234", "1-206*") is True
        assert match_pattern("+12065551234", "(206)*") is True

    @pytest.mark.unit
    def test_empty_pattern(self) -> None:
        """Empty pattern returns False."""
        assert match_pattern("+12065551234", "") is False

    @pytest.mark.unit
    def test_only_wildcard(self) -> None:
        """Pattern with only wildcard matches everything."""
        assert match_pattern("+12065551234", "*") is True

    @pytest.mark.unit
    def test_invalid_number_raises(self) -> None:
        """Invalid number raises ValidationError."""
        with pytest.raises(ValidationError):
            match_pattern("123", "123")

    @pytest.mark.unit
    def test_number_with_various_formats(self) -> None:
        """Pattern matching works with various number formats."""
        # All these should match the same number
        assert match_pattern("+1 (206) 555-1234", "555") is True
        assert match_pattern("1-206-555-1234", "555") is True
        assert match_pattern("12065551234", "555") is True
