# 7. Technical Considerations

### Architecture Overview

```
┌─────────────────────────────────────────────────────────────────┐
│                        Teams Phone CLI                          │
│  (Python + Typer)                                               │
├─────────────────────────────────────────────────────────────────┤
│  CLI Layer (Typer)                                              │
│  ├── users commands                                             │
│  ├── numbers commands                                           │
│  ├── policies commands                                          │
│  ├── locations commands                                         │
│  ├── tenants commands                                           │
│  └── auth commands                                              │
├─────────────────────────────────────────────────────────────────┤
│  Service Layer                                                  │
│  ├── UserService        (Graph API)                             │
│  ├── NumberService      (Graph API + async polling)             │
│  ├── PolicyService      (Graph API + CSV)                       │
│  ├── LocationService    (CSV only)                              │
│  ├── TenantService      (Local config)                          │
│  └── AuthService        (MSAL)                                  │
├─────────────────────────────────────────────────────────────────┤
│  Infrastructure Layer                                           │
│  ├── GraphClient        (HTTP client, retry, throttling)        │
│  ├── CacheManager       (Token cache, data cache, CSV cache)    │
│  ├── ConfigManager      (TOML config, tenant profiles)          │
│  └── OutputFormatter    (Tables, JSON, progress bars)           │
└─────────────────────────────────────────────────────────────────┘
            │                                    │
            ▼                                    ▼
┌──────────────────────────┐      ┌──────────────────────────────┐
│   Microsoft Graph API    │      │   Local CSV Cache            │
│   (Beta endpoints)       │      │   ~/.teams-phone/cache/      │
│   - /admin/teams/*       │      │   ├── locations.csv          │
│                          │      │   ├── policies.csv           │
│                          │      │   └── export-metadata.json   │
└──────────────────────────┘      └──────────────────────────────┘
```

### API Specifications

#### Graph API Endpoints (Beta)

| Operation | Endpoint | Method |
|-----------|----------|--------|
| List user configs | `/admin/teams/userConfigurations` | GET |
| Get user config | `/admin/teams/userConfigurations/{id}` | GET |
| List numbers | `/admin/teams/telephoneNumberManagement/numberAssignments` | GET |
| Assign number | `/admin/teams/telephoneNumberManagement/numberAssignments/assignNumber` | POST |
| Unassign number | `/admin/teams/telephoneNumberManagement/numberAssignments/unassignNumber` | POST |
| Update number | `/admin/teams/telephoneNumberManagement/numberAssignments/updateNumber` | POST |
| Poll operation | `/admin/teams/telephoneNumberManagement/operations/{id}` | GET |
| Assign policy | `/admin/teams/policy/userAssignments/assign` | POST |

#### Phone Number Format Normalization

```python
# Input: E.164 format (+12065551234) or digits (12065551234)
# assignNumber/unassignNumber: digits only (12065551234)
# updateNumber: E.164 with + prefix (+12065551234)

def normalize_for_assign(number: str) -> str:
    return number.lstrip('+')

def normalize_for_update(number: str) -> str:
    if not number.startswith('+'):
        return f'+{number}'
    return number
```

#### Async Operation Polling

```python
# POST to assignNumber/unassignNumber returns 202 Accepted
# Location header: .../operations('{operation_id}')

async def poll_operation(operation_id: str, timeout: int = 60) -> OperationResult:
    start = time.time()
    while time.time() - start < timeout:
        result = await graph_client.get(f'/admin/teams/telephoneNumberManagement/operations/{operation_id}')
        if result['status'] in ('succeeded', 'failed', 'skipped'):
            return result
        await asyncio.sleep(2 + random.uniform(0, 1))  # 2-3 second intervals
    raise TimeoutError(f"Operation {operation_id} did not complete within {timeout}s")
```

### Configuration Schema

```toml
# ~/.teams-phone/config.toml

[default]
tenant = "contoso"
output_format = "table"
color = true

[cache]
users_ttl = 300      # 5 minutes
numbers_ttl = 300    # 5 minutes
policies_ttl = 600   # 10 minutes

[tenants.contoso]
tenant_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
client_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
display_name = "Contoso Ltd"
auth_method = "certificate"
cert_path = "/path/to/contoso-cert.pem"
default_location = "Seattle-HQ"

[tenants.fabrikam]
tenant_id = "yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy"
client_id = "yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy"
display_name = "Fabrikam Inc"
auth_method = "client-secret"
# secret via TEAMS_PHONE_CLIENT_SECRET env var
```

### CSV Cache Schemas

**locations.csv:**
```csv
locationId,civicAddressId,description,companyName,address,city,stateOrProvince,postalCode,countryOrRegion,isDefault
93cb8a70-...,a1b2c3d4-...,Main Office - Floor 3,Contoso Ltd,123 Main St,Seattle,WA,98101,US,true
```

**policies.csv:**
```csv
policyType,policyName,policyId,description,isGlobal
TeamsCallingPolicy,Global,Global,Default calling policy,true
TeamsCallingPolicy,AllowCalling,AllowCalling,Allows all calling features,false
```

### Dependencies

| Dependency | Purpose | Version |
|------------|---------|---------|
| typer | CLI framework | ^0.9.0 |
| httpx | Async HTTP client | ^0.25.0 |
| msal | Microsoft authentication | ^1.24.0 |
| rich | Terminal formatting | ^13.0.0 |
| pydantic | Data validation | ^2.0.0 |
| toml | Config file parsing | ^0.10.0 |

### External Dependencies

| System | Requirement | Purpose |
|--------|-------------|---------|
| Microsoft Entra ID | App Registration | Service principal for Graph API access |
| Microsoft Graph API | Beta endpoints | Teams Phone management operations |
| PowerShell + MicrosoftTeams module | Optional | CSV export for locations and policies |

### Testing Strategy

| Layer | Approach | Coverage Target |
|-------|----------|-----------------|
| Unit Tests | pytest, mock Graph responses | > 80% |
| Integration Tests | Test tenant with sample data | Core workflows |
| Contract Tests | Schema snapshots validated against live API (daily CI) | All Graph endpoints |
| E2E Tests | Real API calls (separate test tenant) | Smoke tests |
| Load Tests | Bulk operations with 1000 rows | Performance validation |

---

