# Japan Post ZIP Code Address Lookup

This feature provides Japanese address auto-completion using the Japan Post Digital Address API. When users enter a ZIP code, the system automatically looks up and populates prefecture, city, and town fields in both Japanese and romanized (English) formats.

## Environment Variables

Add these to your `.env` file:

```bash
# Japan Post Digital Address API
JAPAN_POST_API_URL=https://api.da.posta.japanpost.jp
JAPAN_POST_CLIENT_ID=your_client_id
JAPAN_POST_CLIENT_SECRET=your_client_secret
JAPAN_POST_TIMEOUT=10000  # Optional, defaults to 10000ms
```

Contact Japan Post to obtain API credentials for the Digital Address service.

## Architecture Overview

```
┌─────────────────────────────────────────────────────────────────────┐
│                           Portal (Next.js)                          │
├─────────────────────────────────────────────────────────────────────┤
│  ZipCodeInput ─► useZipCodeLookup ─► addressService.lookupByZipCode │
│       │                                         │                   │
│       ▼                                         ▼                   │
│  JapanAddressForm                    GET /api/address/lookup/zip/:zip│
└─────────────────────────────────────────────────────────────────────┘
                                                  │
                                                  ▼
┌─────────────────────────────────────────────────────────────────────┐
│                            BFF (NestJS)                             │
├─────────────────────────────────────────────────────────────────────┤
│  AddressController ─► JapanPostAddressService                       │
│         │                       │                                   │
│         │                       ▼                                   │
│         │            JapanPostConnectionService                     │
│         │                  (OAuth token caching)                    │
│         │                       │                                   │
│         ▼                       ▼                                   │
│  PATCH /me/address/bilingual   Japan Post API                       │
│         │                                                           │
│         ▼                                                           │
│  UserProfileService.updateBilingualAddress()                        │
│         │                                                           │
│         ├──► WHMCS (English address - source of truth)              │
│         └──► Salesforce (Japanese address - secondary)              │
└─────────────────────────────────────────────────────────────────────┘
```

## API Endpoints

### Public Endpoints (No Auth Required)

| Endpoint                           | Method | Description                 | Rate Limit |
| ---------------------------------- | ------ | --------------------------- | ---------- |
| `/api/address/lookup/zip/:zipCode` | GET    | Look up address by ZIP code | 30/min     |
| `/api/address/status`              | GET    | Check service availability  | -          |

### Authenticated Endpoints

| Endpoint                    | Method | Description                                          |
| --------------------------- | ------ | ---------------------------------------------------- |
| `/api/me/address/bilingual` | PATCH  | Update address with dual-write to WHMCS + Salesforce |

## Field Mappings

### WHMCS (English/Romanized)

| WHMCS Field | Source                                                             |
| ----------- | ------------------------------------------------------------------ |
| `address1`  | `{buildingName} {roomNumber}` (for apartments) or `{buildingName}` |
| `address2`  | `town` (romanized street/block)                                    |
| `city`      | `city` (romanized)                                                 |
| `state`     | `prefecture` (romanized)                                           |
| `postcode`  | ZIP code                                                           |
| `country`   | "JP"                                                               |

### Salesforce Contact (Japanese)

| Salesforce Field    | Source                    |
| ------------------- | ------------------------- |
| `MailingStreet`     | `townJa` (Japanese)       |
| `MailingCity`       | `cityJa` (Japanese)       |
| `MailingState`      | `prefectureJa` (Japanese) |
| `MailingPostalCode` | ZIP code                  |
| `MailingCountry`    | "Japan"                   |
| `BuildingName__c`   | Building name (English)   |
| `RoomNumber__c`     | Room number               |

## Components

### ZipCodeInput

Auto-lookup ZIP code input with visual feedback.

```tsx
import { ZipCodeInput } from "@/features/address";

<ZipCodeInput
  value={zipCode}
  onChange={setZipCode}
  onAddressFound={address => {
    // address contains both Japanese and romanized fields
    console.log(address.prefecture, address.prefectureJa);
  }}
/>;
```

### JapanAddressForm

Complete address form with residence type selection.

```tsx
import { JapanAddressForm } from "@/features/address";

<JapanAddressForm
  onChange={(data, isComplete) => {
    // data is BilingualAddress type
    // isComplete is true when all required fields are filled
  }}
/>;
```

### AddressStepJapan

Drop-in replacement for signup/registration flows.

```tsx
import { AddressStepJapan } from "@/features/address";

<AddressStepJapan
  form={form}
  onJapaneseAddressChange={bilingualData => {
    // Capture Japanese fields for Salesforce sync
  }}
/>;
```

## Dual-Write Behavior

When updating addresses via `PATCH /me/address/bilingual`:

1. **WHMCS Update (Blocking)**: English address is written to WHMCS as source of truth
2. **Salesforce Update (Non-Blocking)**: Japanese address is written to Salesforce
   - If Salesforce update fails, it's logged but doesn't fail the request
   - If no Salesforce mapping exists, update is skipped silently

## Residence Type

Users select either:

- **House**: Building name optional, no room number
- **Apartment/Mansion**: Building name optional, room number required

The `address1` field format changes based on residence type:

- House: `{buildingName}` or empty
- Apartment: `{buildingName} {roomNumber}`

## Manual Address Entry

If users skip ZIP code lookup and enter addresses manually:

| Scenario                    | WHMCS                 | Salesforce                            |
| --------------------------- | --------------------- | ------------------------------------- |
| ZIP lookup used             | English fields filled | Japanese fields filled                |
| Manual entry (no ZIP)       | English fields filled | **Japanese fields empty**             |
| Manual then ZIP lookup used | English updated       | Japanese fields populated from lookup |

**Important**: Manual address entry will work for WHMCS but Salesforce will receive empty Japanese address fields. The system does NOT perform reverse lookups on manually entered addresses.

**Recommendation**: Encourage users to enter ZIP code first to get proper Japanese address data for Salesforce.

## Residence Type Selection

Users must explicitly choose their residence type:

- **House**: Building name optional, no room number field shown
- **Apartment/Mansion**: Building name optional, room number **required**

The form does not default to either option - users must select one to proceed.

## Configuration Validation

On BFF startup, the service validates environment variables and logs errors:

```
# Missing configuration example log:
ERROR Japan Post API configuration is invalid. Address lookup will be unavailable.
  errors: [
    { envVar: "JAPAN_POST_API_URL", message: "Missing required environment variable" },
    { envVar: "JAPAN_POST_CLIENT_ID", message: "Missing required environment variable" }
  ]
  hint: "Add the required environment variables to your .env file"
```

The service will throw clear errors if called without proper configuration.

## Error Handling

- Invalid ZIP codes return empty results (no error)
- Japan Post API timeouts return 500 error
- Rate limiting returns 429 with retry-after header
- Missing/invalid configuration throws descriptive error on API call
