# Task ID: 10

**Title:** Implement Phone Number Utilities

**Status:** pending

**Dependencies:** 1, 2

**Priority:** medium

**Description:** Create utility functions for phone number normalization, validation, and format conversion between Graph API formats.

**Details:**

Create `src/teams_phone/utils.py`:
```python
import re

def normalize_phone_number(number: str) -> str:
    """Strip non-digits, validate E.164 format."""
    digits = re.sub(r'\D', '', number)
    if not digits or len(digits) < 10:
        raise ValidationError(f"Invalid phone number: {number}")
    return digits

def format_for_assign(number: str) -> str:
    """Format for assignNumber/unassignNumber: digits only."""
    return normalize_phone_number(number)

def format_for_update(number: str) -> str:
    """Format for updateNumber: E.164 with + prefix."""
    digits = normalize_phone_number(number)
    return f"+{digits}"

def format_for_display(number: str) -> str:
    """Format for user display: +1 (206) 555-1234."""
    ...

def match_pattern(number: str, pattern: str) -> bool:
    """Match phone number against search pattern with wildcards."""
    ...
```

**Test Strategy:**

Unit tests: various input formats (with/without +, spaces, dashes), validation errors for invalid numbers, pattern matching with wildcards.

---

## Implementation Notes

### Session: 2026-01-29 - Agent: claude-sonnet-4-5-20250929

**Status:** Implemented

#### What Was Done
- Implemented all 5 utility functions in `src/teams_phone/utils.py`
- Created comprehensive unit test suite in `tests/unit/test_utils.py` (40 tests)
- All functions follow strict mypy typing and Google-style docstrings

#### Files Changed
- Modified: `src/teams_phone/utils.py` - Implemented all functions
- Created: `tests/unit/test_utils.py` - Full test coverage

#### Key Decisions Made
- **10-digit minimum validation**: Chose to validate after stripping ALL non-digits, which means country code is included in the count. This allows both 10-digit US numbers (without country code) and 11-digit US numbers (with country code 1).
- **International number display**: For non-NANP numbers (not 10 or 11 US digits), `format_for_display` returns E.164 format (`+{digits}`) rather than attempting complex international formatting. This is a documented limitation.
- **Wildcard pattern matching uses `re.search`**: Initially used `re.fullmatch` which failed for patterns like `206*` against `12065551234` (starts with country code). Changed to `re.search` so wildcard patterns can match anywhere in the number, which is more intuitive for area code searches.

#### Patterns Discovered
- **ValidationError import**: From `teams_phone.exceptions` (see `exceptions.py:85-95`)
- **Test class organization**: Follow `TestClassName` with `@pytest.mark.unit` decorator pattern (see `test_exceptions.py`)
- **Error with remediation**: ValidationError accepts `remediation` kwarg for user guidance

#### API Format Reference (from Graph API contracts)
| Endpoint | Format Example | Use Function |
|----------|----------------|--------------|
| assignNumber | `12061234567` | `format_for_assign()` |
| unassignNumber | `12061234567` | `format_for_assign()` |
| updateNumber | `+12061234567` | `format_for_update()` |
| User display | `+1 (206) 555-1234` | `format_for_display()` |

#### Open Items for Next Agent
- [ ] Consider adding `phonenumbers` library if more sophisticated international formatting is needed
- [ ] May want to add country code detection for proper international display formatting

#### Gotchas and Warnings
- **NANP assumption**: `format_for_display` assumes 10-digit numbers are US/Canada and adds +1 prefix. This might not be correct for all 10-digit international numbers.
- **Wildcard pattern matching**: Patterns like `206*` match ANYWHERE in the number (not just at the start), which is intentional for user-friendly area code searching.

---
