import { ApiProperty } from '@nestjs/swagger';
import { JoiSchema, JoiSchemaOptions } from 'nestjs-joi';
import * as Joi from 'joi';
import { IsEmail, IsIn, IsNotEmpty, ValidateIf } from 'class-validator';

/**
 * Enumeration of supported login modes
 *
 * Defines the different methods a user can use to identify themselves during login.
 * This allows flexible authentication supporting multiple identifier types.
 *
 * @enum
 * @example LOGIN_MODES.EMAIL // Login using email address
 * @example LOGIN_MODES.PHONE // Login using phone number
 * @example LOGIN_MODES.UID   // Login using unique system identifier
 */
export enum LOGIN_MODES {
  /** Login using a unique system-generated identifier */
  UID = 'uid',

  /** Login using a phone number */
  PHONE = 'phone',

  /** Login using an email address (most common method) */
  EMAIL = 'email',
}

/**
 * LoginUserDto - Flexible user authentication Data Transfer Object
 *
 * This DTO supports multiple login methods with conditional validation.
 * Depending on the selected login_mode, different fields become required.
 *
 * Features:
 * - Multiple login identifier options (email, phone, unique_id)
 * - Conditional validation based on login_mode
 * - Joi schema validation with automatic sanitization
 * - OpenAPI/Swagger documentation
 * - Phone number format validation
 * - Country code validation for phone login
 *
 * @example
 * // Login with email
 * {
 *   "email": "student@example.com",
 *   "password": "wosa@699",
 *   "login_mode": "email"
 * }
 *
 * @example
 * // Login with phone number
 * {
 *   "mobile": "9876543210",
 *   "country_iso3_code": "IN",
 *   "password": "wosa@699",
 *   "login_mode": "phone"
 * }
 *
 * @example
 * // Login with unique ID
 * {
 *   "unique_id": "000001DF",
 *   "password": "wosa@699",
 *   "login_mode": "uid"
 * }
 */
@JoiSchemaOptions({
  /**
   * Strip unknown fields from the request payload
   * Removes any fields not defined in the schema for security
   */
  stripUnknown: true,

  /**
   * Abort validation on first error
   * Returns immediately when validation fails for better performance
   */
  abortEarly: true,
})
export class LoginUserDto {
  /**
   * Email address for email-based login
   *
   * Required only when login_mode is 'email'
   * Should be validated for format and existence in the system
   *
   * @optional Conditional based on login_mode
   * @example "student@example.com"
   */
  @ApiProperty({
    description: 'The email of the student',
    example: 'example@example.com',
    required: true,
  })
  @ValidateIf((o) => o.login_mode === LOGIN_MODES.EMAIL)
  @IsNotEmpty({ message: 'Email is required for email login mode' })
  @IsEmail({}, { message: 'Please provide a valid email address' })
  @JoiSchema(
    Joi.string()
      .trim() // Remove leading/trailing whitespace
      .email() // Validate email format
      .required() // Required when login_mode is EMAIL
      .messages({
        'string.email': 'Please provide a valid email address',
        'any.required': 'Email is required for email login',
      }),
  )
  email: string;

  /**
   * Unique system identifier for UID-based login
   *
   * Required only when login_mode is 'uid'
   * Typically a system-generated ID like student ID or custom identifier
   *
   * @optional Conditional based on login_mode
   * @example "000001DF"
   * @example "STU2024001"
   */
  @ApiProperty({
    description: 'The unique id of the student',
    example: '000001DF',
    required: true,
  })
  @ValidateIf((o) => o.login_mode === LOGIN_MODES.UID)
  @IsNotEmpty({ message: 'Unique ID is required for UID login mode' })
  @JoiSchema(
    Joi.string()
      .trim() // Remove leading/trailing whitespace
      .required() // Required when login_mode is UID
      .messages({
        'any.required': 'Unique ID is required for UID login',
        'string.empty': 'Unique ID cannot be empty',
      }),
  )
  unique_id: string;

  /**
   * Mobile phone number for phone-based login
   *
   * Required only when login_mode is 'phone'
   * Validates 10-digit format (adjust pattern for international numbers)
   *
   * @warning 10-digit pattern is specific to certain countries (like India)
   *          Consider more flexible international phone validation
   *
   * @optional Conditional based on login_mode
   * @example "9876543210"
   */
  @ApiProperty({
    description: 'The mobile number of the student',
    example: '9876543210',
    required: true,
  })
  @ValidateIf((o) => o.login_mode === LOGIN_MODES.PHONE)
  @IsNotEmpty({ message: 'Mobile number is required for phone login mode' })
  @JoiSchema(
    Joi.string()
      .trim()
      .pattern(/^[0-9]{10}$/) // 10-digit phone number validation
      .required()
      .messages({
        'string.pattern.base': 'Mobile number must be 10 digits',
        'any.required': 'Mobile number is required for phone login',
      }),
  )
  mobile: string;

  /**
   * Country ISO3 code for phone-based login
   *
   * Required only when login_mode is 'phone'
   * 2-letter ISO country code (ISO 3166-1 alpha-2)
   *
   * @note Field name suggests ISO3 but length(2) expects ISO2
   *       Consider renaming to country_code or using ISO3 format
   *
   * @optional Conditional based on login_mode
   * @example "IN" (India)
   * @example "US" (United States)
   */
  @ApiProperty({
    description: 'The country code of the student',
    example: 'IN',
    required: true,
  })
  @ValidateIf((o) => o.login_mode === LOGIN_MODES.PHONE)
  @IsNotEmpty({ message: 'Country code is required for phone login mode' })
  @JoiSchema(
    Joi.string()
      .trim()
      .length(2) // ISO 3166-1 alpha-2 format (2 letters)
      .required()
      .messages({
        'string.length': 'Country code must be 2 letters',
        'any.required': 'Country code is required for phone login',
      }),
  )
  country_iso3_code: string;

  /**
   * Account password for authentication
   *
   * Required for all login modes
   * Should be compared against securely hashed password in database
   *
   * Security considerations:
   * - Minimum length validation (consider @MinLength(8))
   * - Complexity requirements (uppercase, lowercase, numbers, symbols)
   * - Check against breached password databases
   * - Use constant-time comparison to prevent timing attacks
   *
   * @required Always required
   * @example "wosa@699"
   * @example "SecurePass123!"
   */
  @ApiProperty({
    description: 'The password of the student account',
    example: 'wosa@699',
    required: true,
  })
  @JoiSchema(
    Joi.string().required().messages({
      'any.required': 'Password is required',
      'string.empty': 'Password cannot be empty',
    }),
  )
  @IsNotEmpty({ message: 'Password is required' })
  password: string;

  /**
   * Login method/authentication mode
   *
   * Determines which identifier fields are required and which
   * authentication flow to use.
   *
   * @required Always required
   * @example LOGIN_MODES.EMAIL
   * @example LOGIN_MODES.PHONE
   * @example LOGIN_MODES.UID
   */
  @ApiProperty({
    description: 'The login mode of the student account',
    enum: LOGIN_MODES,
    example: LOGIN_MODES.EMAIL,
    required: true,
  })
  @JoiSchema(
    Joi.string()
      .valid(...Object.values(LOGIN_MODES)) // Only allow enum values
      .required()
      .messages({
        'any.only': `login_mode must be one of: ${Object.values(LOGIN_MODES).join(', ')}`,
        'any.required': 'login_mode is required',
      }),
  )
  @IsNotEmpty({ message: 'Login mode is required' })
  @IsIn(Object.values(LOGIN_MODES), { message: 'Invalid login mode' })
  login_mode: string;
}
