import {
  IsString,
  IsNotEmpty,
  IsArray,
  ValidateNested,
  IsOptional,
  ArrayMinSize,
  ValidateIf,
} from 'class-validator';
import { Type } from 'class-transformer';
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';

/**
 * DTO representing a single commission instance.
 * A commission instance defines the type and value of commission applied for a specific set.
 */
class CommissionInstanceDto {
  /**
   * Optional unique identifier for the commission instance.
   * Useful when updating existing instances.
   */
  @ApiPropertyOptional({
    example: '1',
    description: 'Optional commission instance ID',
  })
  @IsString()
  @IsOptional()
  id?: string;

  /**
   * Optional reference for the commission instance.
   * Can be used to map instances to another system.
   */
  @ApiPropertyOptional({
    example: '1',
    description: 'Optional instance reference',
  })
  @IsString()
  @IsOptional()
  instance?: string;

  /**
   * Type of commission applied for this instance.
   * Examples: 'percentage', 'flat'.
   */
  @ApiProperty({ example: 'percentage', description: 'Type of commission' })
  @IsString()
  @IsNotEmpty({ message: 'Commission type is required' })
  commission_type: string;

  /**
   * Value of the commission applied.
   * If type is percentage, this is the percentage value.
   * If type is flat, this is the fixed amount.
   */
  @ApiProperty({
    example: '10',
    description: 'Commission amount or percentage',
  })
  @IsString()
  @IsNotEmpty({ message: 'Commission amount is required' })
  commission_amount: string;
}

/**
 * DTO representing a single commission set.
 * Each set may have multiple commission instances, optional bonus, and attachment files.
 */
class SetDto {
  /**
   * Optional identifier of the set.
   * Useful when updating an existing set.
   */
  @ApiPropertyOptional({ example: '1', description: 'Optional set ID' })
  @IsString()
  @IsOptional()
  id?: string;

  /**
   * Name of the set.
   * Required field to identify the set.
   */
  @ApiProperty({ example: 'Set A', description: 'Name of the commission set' })
  @IsString()
  @IsNotEmpty({ message: 'Set name is required' })
  set_name: string;

  /**
   * Minimum students required in this set.
   * Required only when student_condition === '2'.
   * Uses ValidateIf to conditionally enforce validation.
   */
  @ApiPropertyOptional({
    example: '5',
    description: 'Minimum students required (only if student_condition = 2)',
  })
  @ValidateIf((o, value) => o?.__parentDto?.student_condition === '2')
  @IsString()
  @IsNotEmpty({
    message: 'Minimum students is required when student condition is 2',
  })
  min_students?: string;

  /**
   * Maximum students allowed in this set.
   * Required only when student_condition === '2'.
   */
  @ApiPropertyOptional({
    example: '10',
    description: 'Maximum students allowed (only if student_condition = 2)',
  })
  @ValidateIf((o, value) => o?.__parentDto?.student_condition === '2')
  @IsString()
  @IsNotEmpty({
    message: 'Maximum students is required when student condition is 2',
  })
  max_students?: string;

  /**
   * Indicates if a bonus applies for this set.
   * '1' = yes, '0' = no.
   */
  @ApiProperty({
    example: '1',
    description: 'Indicates if bonus applies (1 = yes, 0 = no)',
  })
  @IsString()
  @IsNotEmpty({ message: 'Bonus selection is required' })
  has_bonus: string;

  /**
   * Bonus type, required if has_bonus === '1'.
   * Example values: 'flat', 'percentage'.
   */
  @ApiPropertyOptional({
    example: 'flat',
    description: 'Bonus type (required if has_bonus = 1)',
  })
  @ValidateIf((o) => o.has_bonus === '1')
  @IsString()
  @IsNotEmpty({ message: 'Bonus type is required when bonus is selected' })
  bonus_type?: string;

  /**
   * Bonus amount or percentage.
   * Required only if has_bonus === '1'.
   */
  @ApiPropertyOptional({
    example: '500',
    description: 'Bonus amount (required if has_bonus = 1)',
  })
  @ValidateIf((o) => o.has_bonus === '1')
  @IsString()
  @IsNotEmpty({
    message: 'Bonus amount / percentage is required when bonus is selected',
  })
  bonus_amount?: string;

  /**
   * Optional specific contract files for this set.
   * Can store JSON references, URLs, or file metadata.
   */
  @ApiPropertyOptional({
    type: [Object],
    description: 'Optional specific contract file(s)',
  })
  @IsOptional()
  @IsArray()
  specific_contract?: any[];

  /**
   * Optional attachment files for this set.
   */
  @ApiPropertyOptional({
    type: [Object],
    description: 'Optional attachment files',
  })
  @IsOptional()
  @IsArray()
  attachment_file?: any[];

  /**
   * List of commission instances in this set.
   * Must contain at least one instance.
   */
  @ApiProperty({
    type: [CommissionInstanceDto],
    description: 'Commission structure instances within this set',
  })
  @IsArray()
  @ArrayMinSize(1, { message: 'At least one commission instance is required' })
  @ValidateNested({ each: true })
  @Type(() => CommissionInstanceDto)
  instances: CommissionInstanceDto[];
}

/**
 * DTO representing an applicable set with conditions like campuses, study levels, courses.
 */
class ApplicableSetDto {
  @ApiPropertyOptional({
    example: '1',
    description: 'Optional applicable set ID',
  })
  @IsString()
  @IsOptional()
  id?: string;

  /**
   * Array of campus IDs this set applies to.
   * Required if campus_condition === '2'.
   */
  @ApiPropertyOptional({
    type: [String],
    description: 'Applicable campuses (required if campus_condition = 2)',
    example: ['campus_1', 'campus_2'],
  })
  @IsOptional()
  @IsArray()
  applicable_campuses?: string[];

  /**
   * Array of study levels this set applies to.
   * Required if study_level_condition === '2'.
   */
  @ApiPropertyOptional({
    type: [String],
    description:
      'Applicable study levels (required if study_level_condition = 2)',
    example: ['bachelor', 'master'],
  })
  @IsOptional()
  @IsArray()
  applicable_study_levels?: string[];

  /**
   * Array of courses this set applies to.
   * Required if course_condition === '2'.
   */
  @ApiPropertyOptional({
    type: [String],
    description: 'Applicable courses (required if course_condition = 2)',
    example: ['course_1', 'course_2'],
  })
  @IsOptional()
  @IsArray()
  applicable_courses?: string[];

  /**
   * Total number of instances for this applicable set.
   */
  @ApiProperty({
    example: '2',
    description: 'Number of instances for this set',
  })
  @IsString()
  @IsNotEmpty({ message: 'Number of instances is required' })
  no_of_instances: string;

  /**
   * Number of students per instance.
   */
  @ApiProperty({ example: '5', description: 'Number of students per instance' })
  @IsString()
  @IsNotEmpty({ message: 'Students per instance is required' })
  students_per: string;

  /**
   * Array of sets under this applicable set.
   * Must contain at least one set.
   */
  @ApiProperty({
    type: [SetDto],
    description: 'List of commission sets under this applicable set',
  })
  @IsArray()
  @ArrayMinSize(1, { message: 'At least one set is required' })
  @ValidateNested({ each: true })
  @Type(() => SetDto)
  sets: SetDto[];
}

/**
 * DTO for creating a commission contract.
 * Includes product, association type, visa service, institute, client countries,
 * student conditions, and applicable sets.
 */
export class CreateCommissionContractDto {
  @ApiPropertyOptional({ example: '1', description: 'Optional contract ID' })
  @IsString()
  @IsOptional()
  id?: string;

  /**
   * Product associated with this contract.
   * Required.
   */
  @ApiProperty({ example: 'prod_101', description: 'Selected product ID' })
  @IsString()
  @IsNotEmpty({ message: 'Product is required' })
  product_id: string;

  /**
   * Type of associate (e.g., Sub-agent, Institute).
   * Required.
   */
  @ApiProperty({ example: 'associate_type', description: 'Association type' })
  @IsString()
  @IsNotEmpty({ message: 'Associate is required' })
  association_type: string;

  /**
   * Visa service country for this contract.
   * Required.
   */
  @ApiProperty({
    example: 'country_visa_01',
    description: 'Visa service country ID',
  })
  @IsString()
  @IsNotEmpty({ message: 'Visa Service is required' })
  visa_service_country_id: string;

  /**
   * Optional associate ID.
   */
  @ApiPropertyOptional({
    example: 'assoc_123',
    description: 'Optional associate ID',
  })
  @IsString()
  @IsOptional()
  associate_id?: string;

  /**
   * Institute ID this contract applies to.
   * Required.
   */
  @ApiProperty({ example: '123', description: 'Institute ID' })
  @IsString()
  @IsNotEmpty({ message: 'Institute is required' })
  institute_id: string;

  /**
   * List of client originating countries.
   * Must contain at least one.
   */
  @ApiProperty({
    type: [String],
    example: ['IN', 'AU'],
    description: 'Client originating countries for this contract',
  })
  @IsArray()
  @ArrayMinSize(1, { message: 'At least one country required' })
  client_from_countries: string[];

  /**
   * Optional general attachments for the contract.
   */
  @ApiPropertyOptional({
    type: [Object],
    description: 'Optional general contract file(s)',
  })
  @IsOptional()
  @IsArray()
  attachment_file?: any[];

  /**
   * Student condition flag (1 or 2).
   * Determines validation requirements for min/max students.
   */
  @ApiProperty({ example: '1', description: 'Student condition flag (1 or 2)' })
  @IsString()
  @IsNotEmpty({ message: 'Student Condition is required' })
  student_condition: string;

  /**
   * Campus condition flag (1 or 2).
   * If 2, applicable_campuses must be provided in applicable_sets.
   */
  @ApiProperty({ example: '2', description: 'Campus condition flag (1 or 2)' })
  @IsString()
  @IsNotEmpty({ message: 'Campus Condition is required' })
  campus_condition: string;

  /**
   * Study level condition flag (1 or 2).
   * If 2, applicable_study_levels must be provided in applicable_sets.
   */
  @ApiProperty({
    example: '2',
    description: 'Study level condition flag (1 or 2)',
  })
  @IsString()
  @IsNotEmpty({ message: 'Study Level Condition is required' })
  study_level_condition: string;

  /**
   * Course condition flag (1 or 2).
   * If 2, applicable_courses must be provided in applicable_sets.
   */
  @ApiProperty({ example: '1', description: 'Course condition flag (1 or 2)' })
  @IsString()
  @IsNotEmpty({ message: 'Course Condition is required' })
  course_condition: string;

  /**
   * Notes or important conditions for this contract.
   * Required.
   */
  @ApiProperty({
    example: 'Students must maintain 70% attendance',
    description: 'Important notes or conditions',
  })
  @IsString()
  @IsNotEmpty({ message: 'Important Conditions / Notes is required' })
  notes: string;

  /**
   * Currency for this commission contract.
   * Required (e.g., USD, INR).
   */
  @ApiProperty({ example: 'USD', description: 'Currency code' })
  @IsString()
  @IsNotEmpty({ message: 'Currency is required' })
  currency: string;

  /**
   * Array of applicable sets for this contract.
   * Must contain at least one.
   */
  @ApiProperty({
    type: [ApplicableSetDto],
    description: 'List of applicable sets with conditions',
  })
  @IsArray()
  @ArrayMinSize(1, { message: 'At least one applicable set is required' })
  @ValidateNested({ each: true })
  @Type(() => ApplicableSetDto)
  applicable_sets: ApplicableSetDto[];
}
