import {
  Controller,
  Get,
  Patch,
  Body,
  Req,
  UseInterceptors,
  ClassSerializerInterceptor,
  UseGuards,
} from "@nestjs/common";
import { UsersService } from "./application/users.service.js";
import { createZodDto, ZodResponse, ZodSerializerDto } from "nestjs-zod";
import { updateCustomerProfileRequestSchema } from "@customer-portal/domain/auth";
import { addressSchema, userSchema } from "@customer-portal/domain/customer";
import { bilingualAddressSchema } from "@customer-portal/domain/address";
import type { Address, User } from "@customer-portal/domain/customer";
import type { RequestWithUser, RequestWithOptionalUser } from "@bff/modules/auth/auth.types.js";
import { SalesforceReadThrottleGuard } from "@bff/integrations/salesforce/guards/salesforce-read-throttle.guard.js";
import { OptionalAuth } from "@bff/modules/auth/decorators/public.decorator.js";

class UpdateAddressDto extends createZodDto(addressSchema.partial()) {}
class UpdateBilingualAddressDto extends createZodDto(bilingualAddressSchema) {}
class UpdateCustomerProfileRequestDto extends createZodDto(updateCustomerProfileRequestSchema) {}
class AddressDto extends createZodDto(addressSchema) {}
class UserDto extends createZodDto(userSchema) {}

@Controller("me")
@UseInterceptors(ClassSerializerInterceptor)
export class UsersController {
  constructor(private usersService: UsersService) {}

  /**
   * GET /me - Get complete customer profile (includes address)
   * Profile data fetched from WHMCS (single source of truth)
   *
   * Uses @OptionalAuth: returns null if not logged in, 401 only if session expired
   */
  @OptionalAuth()
  @UseGuards(SalesforceReadThrottleGuard)
  @Get()
  @ZodSerializerDto(userSchema.nullable())
  async getProfile(@Req() req: RequestWithOptionalUser): Promise<User | null> {
    if (!req.user) {
      return null;
    }
    return this.usersService.getProfile(req.user.id);
  }

  /**
   * GET /me/address - Get customer address only
   */
  @UseGuards(SalesforceReadThrottleGuard)
  @Get("address")
  @ZodSerializerDto(addressSchema.nullable())
  async getAddress(@Req() req: RequestWithUser): Promise<Address | null> {
    return this.usersService.getAddress(req.user.id);
  }

  /**
   * PATCH /me/address - Update address fields
   */
  @Patch("address")
  @ZodResponse({ description: "Update address", type: AddressDto })
  async updateAddress(
    @Req() req: RequestWithUser,
    @Body() address: UpdateAddressDto
  ): Promise<Address> {
    return this.usersService.updateAddress(req.user.id, address);
  }

  /**
   * PATCH /me/address/bilingual - Update address with Japanese + English fields
   *
   * Dual-write: English to WHMCS (source of truth), Japanese to Salesforce
   *
   * Use this endpoint when you have both Japanese and English address data
   * (e.g., from Japan Post ZIP code lookup).
   *
   * @example
   * {
   *   "postcode": "1000001",
   *   "prefecture": "Tokyo",
   *   "prefectureJa": "東京都",
   *   "city": "Chiyoda-ku",
   *   "cityJa": "千代田区",
   *   "town": "Chiyoda",
   *   "townJa": "千代田",
   *   "buildingName": "Example Building",
   *   "roomNumber": "101",
   *   "residenceType": "apartment"
   * }
   */
  @Patch("address/bilingual")
  @ZodResponse({ description: "Update bilingual address", type: AddressDto })
  async updateBilingualAddress(
    @Req() req: RequestWithUser,
    @Body() address: UpdateBilingualAddressDto
  ): Promise<Address> {
    return this.usersService.updateBilingualAddress(req.user.id, address);
  }

  /**
   * PATCH /me - Update customer profile (can update profile fields and/or address)
   * All fields optional - only send what needs to be updated
   * Updates stored in WHMCS (single source of truth)
   *
   * Examples:
   *  - Update name only: { firstname: "John", lastname: "Doe" }
   *  - Update address only: { address1: "123 Main St", city: "Tokyo" }
   *  - Update both: { firstname: "John", address1: "123 Main St" }
   */
  @Patch()
  @ZodResponse({ description: "Update profile", type: UserDto })
  async updateProfile(
    @Req() req: RequestWithUser,
    @Body() updateData: UpdateCustomerProfileRequestDto
  ) {
    return this.usersService.updateProfile(req.user.id, updateData);
  }
}
