/**
 * Address Domain - Schemas
 *
 * Zod validation schemas for address lookup and bilingual address data.
 * Types are derived from schemas (Schema-First Approach).
 */

import { z } from "zod";
import { truncate } from "../toolkit/formatting/text.js";
import { ADDRESS_INPUT_LIMITS, STREET_ADDRESS_PATTERN, WHMCS_ADDRESS_LIMITS } from "./constants.js";

// ============================================================================
// ZIP Code Schemas
// ============================================================================

/**
 * Japanese ZIP code schema
 * Accepts: "1000001", "100-0001" -> normalizes to "1000001"
 */
export const zipCodeSchema = z
  .string()
  .regex(/^\d{3}-?\d{4}$/, "ZIP code must be 7 digits (e.g., 100-0001)")
  .transform(val => val.replace(/-/g, ""));

/**
 * ZIP code lookup request
 */
export const zipCodeLookupRequestSchema = z.object({
  zipCode: zipCodeSchema,
});

// ============================================================================
// Japan Post Address Schemas
// ============================================================================

/**
 * Address from Japan Post API lookup
 * Contains both Japanese and romanized versions
 */
export const japanPostAddressSchema = z.object({
  zipCode: z.string(),
  // Japanese (for Salesforce)
  prefecture: z.string(),
  prefectureKana: z.string().optional(),
  city: z.string(),
  cityKana: z.string().optional(),
  town: z.string(),
  townKana: z.string().optional(),
  // Romanized (for WHMCS)
  prefectureRoma: z.string(),
  cityRoma: z.string(),
  townRoma: z.string(),
});

/**
 * Address lookup result containing multiple potential matches
 */
export const addressLookupResultSchema = z.object({
  zipCode: z.string(),
  addresses: z.array(japanPostAddressSchema),
  count: z.number(),
});

// ============================================================================
// Street Address Detail (Chome/Banchi/Go)
// ============================================================================

/**
 * Street address detail schema (chome/banchi/go)
 * Only accepts hyphenated number format: "1-5-3", "1-5", "15-3"
 *
 * Format: {chome}-{banchi}-{go} or {chome}-{banchi}
 * - chome: Block district number (1-99)
 * - banchi: Block number (1-999)
 * - go: Building/house number (1-999, optional)
 */
export const streetAddressDetailSchema = z
  .string()
  .min(1, "Street address is required")
  .max(20, "Street address is too long")
  .regex(
    new RegExp(`^${STREET_ADDRESS_PATTERN.source}$`),
    "Use format like 1-5-3 (chome-banchi-go) or 1-5 (chome-banchi)"
  );

// ============================================================================
// Bilingual Address Schemas (Extended from customer/addressSchema)
// ============================================================================

/**
 * Building information for Japanese addresses.
 * Field lengths are constrained to ensure composed values fit within WHMCS limits.
 */
export const buildingInfoSchema = z.object({
  buildingName: z.string().max(ADDRESS_INPUT_LIMITS.BUILDING_NAME_MAX).optional().nullable(),
  roomNumber: z.string().max(ADDRESS_INPUT_LIMITS.ROOM_NUMBER_MAX).optional().nullable(),
  residenceType: z.enum(["house", "apartment"]),
});

/**
 * Extended address data with bilingual fields
 * Used when updating address in both WHMCS (English) and Salesforce (Japanese)
 *
 * Field lengths are constrained to ensure composed values fit within WHMCS limits.
 * @see ADDRESS_INPUT_LIMITS in constants.ts for the calculated limits
 */
export const bilingualAddressSchema = z.object({
  // ZIP code
  postcode: z.string(),

  // English/Romanized (for WHMCS)
  prefecture: z.string(), // romanized
  city: z.string(), // romanized
  town: z.string(), // romanized

  // Japanese (for Salesforce)
  prefectureJa: z.string(),
  cityJa: z.string(),
  townJa: z.string(),

  // Street address detail (chome/banchi/go) - e.g., "1-5-3" or "1丁目5番3号"
  streetAddress: streetAddressDetailSchema,

  // Building info (same for both systems)
  // Limits ensure address1 (building + room) fits in WHMCS 200-char limit
  buildingName: z.string().max(ADDRESS_INPUT_LIMITS.BUILDING_NAME_MAX).optional().nullable(),
  roomNumber: z.string().max(ADDRESS_INPUT_LIMITS.ROOM_NUMBER_MAX).optional().nullable(),
  residenceType: z.enum(["house", "apartment"]),
});

/**
 * Address update request for profile/signup
 * Combines bilingual address data for dual-write to WHMCS + Salesforce
 */
export const addressUpdateRequestSchema = bilingualAddressSchema.extend({
  country: z.literal("JP").default("JP"),
  countryCode: z.literal("JP").default("JP"),
});

// ============================================================================
// WHMCS Address Mapping
// ============================================================================

/**
 * Prepare address fields for WHMCS update
 * Maps bilingual address to WHMCS field format
 *
 * WHMCS field mapping:
 * - address1: Building name + Room number (e.g., "Sunshine Mansion 101")
 * - address2: Town + Street address (e.g., "Higashiazabu 1-5-3")
 * - city: City (romanized)
 * - state: Prefecture (romanized)
 *
 * Includes safety truncation to ensure fields fit WHMCS limits.
 * This should rarely trigger if input validation works correctly.
 */
export function prepareWhmcsAddressFields(address: BilingualAddress): WhmcsAddressFields {
  const buildingPart = address.buildingName || "";
  const roomPart = address.roomNumber || "";

  // address1: Building + Room (for apartments) or just Building (for houses)
  let address1 =
    address.residenceType === "apartment" ? `${buildingPart} ${roomPart}`.trim() : buildingPart;

  // address2: Town + Street address (romanized)
  let address2 = `${address.town} ${address.streetAddress}`.trim();

  // Safety truncation - should rarely trigger if input validation is enforced
  // Using empty suffix to avoid adding "..." which could confuse address parsing
  if (address1.length > WHMCS_ADDRESS_LIMITS.ADDRESS1_MAX) {
    address1 = truncate(address1, WHMCS_ADDRESS_LIMITS.ADDRESS1_MAX, "");
  }
  if (address2.length > WHMCS_ADDRESS_LIMITS.ADDRESS2_MAX) {
    address2 = truncate(address2, WHMCS_ADDRESS_LIMITS.ADDRESS2_MAX, "");
  }

  return {
    address1: address1 || undefined,
    address2: address2 || undefined,
    city: address.city, // romanized city
    state: address.prefecture, // romanized prefecture
    postcode: address.postcode,
    country: "JP",
    countrycode: "JP",
  };
}

// ============================================================================
// Salesforce Contact Address Mapping
// ============================================================================

/**
 * Prepare address fields for Salesforce Contact update
 * Maps bilingual address to Salesforce field format
 *
 * Salesforce field mapping:
 * - MailingStreet: Town + Street address (Japanese)
 * - MailingCity: City (Japanese)
 * - MailingState: Prefecture (Japanese)
 */
export function prepareSalesforceContactAddressFields(
  address: BilingualAddress
): SalesforceContactAddressFields {
  // Combine town and street address for MailingStreet
  // Example: "東麻布1-5-3" or "東麻布1丁目5番3号"
  const mailingStreet = `${address.townJa}${address.streetAddress}`;

  return {
    MailingStreet: mailingStreet,
    MailingCity: address.cityJa, // Japanese city
    MailingState: address.prefectureJa, // Japanese prefecture
    MailingPostalCode: address.postcode,
    MailingCountry: "Japan",
    BuildingName__c: address.buildingName || null,
    RoomNumber__c: address.roomNumber || null,
  };
}

// ============================================================================
// Exported Types
// ============================================================================

export type ZipCode = z.input<typeof zipCodeSchema>;
export type ZipCodeLookupRequest = z.infer<typeof zipCodeLookupRequestSchema>;
export type JapanPostAddress = z.infer<typeof japanPostAddressSchema>;
export type AddressLookupResult = z.infer<typeof addressLookupResultSchema>;
export type StreetAddressDetail = z.infer<typeof streetAddressDetailSchema>;
export type BuildingInfo = z.infer<typeof buildingInfoSchema>;
export type BilingualAddress = z.infer<typeof bilingualAddressSchema>;
export type AddressUpdateRequest = z.infer<typeof addressUpdateRequestSchema>;

/**
 * WHMCS address field structure
 */
export interface WhmcsAddressFields {
  address1?: string | undefined;
  address2?: string | undefined;
  city: string;
  state: string;
  postcode: string;
  country: string;
  countrycode: string;
}

/**
 * Salesforce Contact address field structure
 */
export interface SalesforceContactAddressFields {
  MailingStreet: string;
  MailingCity: string;
  MailingState: string;
  MailingPostalCode: string;
  MailingCountry: string;
  BuildingName__c: string | null;
  RoomNumber__c: string | null;
}
