import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
import { Transform, Type } from 'class-transformer';
import {
  IsArray,
  IsBoolean,
  IsDateString,
  IsEmail,
  IsEnum,
  IsInt,
  IsNotEmpty,
  IsOptional,
  IsString,
  Matches,
  MaxLength,
  Min,
} from 'class-validator';

import { MeetingStatus } from '@/generated/prisma/client';

import { normalizeMeetingStatus } from '../meeting-status';

function toNumberArray(value: unknown): number[] | undefined {
  if (value === undefined || value === null || value === '') {
    return undefined;
  }

  if (Array.isArray(value)) {
    return value.map(Number).filter(Number.isFinite);
  }

  if (typeof value === 'string') {
    return value
      .split(',')
      .map((item) => Number(item.trim()))
      .filter(Number.isFinite);
  }

  const numberValue = Number(value);

  return Number.isFinite(numberValue) ? [numberValue] : undefined;
}

function toTrimmedString(value: unknown): string | null {
  if (typeof value === 'string') {
    const trimmed = value.trim();
    return trimmed.length > 0 ? trimmed : null;
  }

  if (
    typeof value === 'number' ||
    typeof value === 'boolean' ||
    typeof value === 'bigint'
  ) {
    return String(value);
  }

  return null;
}

function toStringArray(value: unknown): string[] | undefined {
  if (value === undefined || value === null || value === '') {
    return undefined;
  }

  if (Array.isArray(value)) {
    return value
      .map((item) => toTrimmedString(item))
      .filter((item): item is string => item !== null);
  }

  if (typeof value === 'string') {
    return value
      .split(',')
      .map((item) => item.trim())
      .filter(Boolean);
  }

  const stringValue = toTrimmedString(value);

  return stringValue ? [stringValue] : undefined;
}

function toOptionalBoolean(value: unknown): unknown {
  if (value === undefined || value === null || value === '') {
    return undefined;
  }

  if (value === true || value === 'true') {
    return true;
  }

  if (value === false || value === 'false') {
    return false;
  }

  return value;
}

export class CreateMeetingDto {
  @ApiProperty({
    example: 'Review Meeting',
    description: 'Meeting title.',
  })
  @IsString()
  @IsNotEmpty()
  @MaxLength(100)
  title!: string;

  @ApiPropertyOptional({
    example: 'Discuss submitted working group issues.',
    description: 'Meeting description.',
  })
  @IsOptional()
  @IsString()
  description?: string;

  @ApiPropertyOptional({
    example: '2026-06-20',
    description:
      'Meeting date in YYYY-MM-DD format. Required when status is Scheduled or Submitted.',
  })
  @IsOptional()
  @IsDateString()
  meetingDate?: string;

  @ApiPropertyOptional({
    example: '09:00',
    description: 'Meeting start time in 24-hour HH:mm format.',
  })
  @IsOptional()
  @Matches(/^([01]\d|2[0-3]):[0-5]\d$/)
  startTime?: string;

  @ApiPropertyOptional({
    example: '10:00',
    description: 'Meeting end time in 24-hour HH:mm format.',
  })
  @IsOptional()
  @Matches(/^([01]\d|2[0-3]):[0-5]\d$/)
  endTime?: string;

  @ApiPropertyOptional({
    example: 'Room A',
    description: 'Meeting location.',
  })
  @IsOptional()
  @IsString()
  location?: string;

  @ApiPropertyOptional({
    example: MeetingStatus.DRAFT,
    enum: MeetingStatus,
    default: MeetingStatus.DRAFT,
    description: 'Meeting workflow status.',
  })
  @IsOptional()
  @Transform(({ value }) => normalizeMeetingStatus(value))
  @IsEnum(MeetingStatus)
  status?: MeetingStatus;

  @ApiPropertyOptional({
    example: false,
    description: 'Whether to send meeting invitations after saving.',
  })
  @IsOptional()
  @Transform(({ value }) => toOptionalBoolean(value))
  @IsBoolean()
  sendInvites?: boolean;

  @ApiPropertyOptional({
    example: 1,
    description: 'Optional meeting request ID linked to this meeting.',
  })
  @IsOptional()
  @Type(() => Number)
  @IsInt()
  @Min(1)
  meetingRequestId?: number;

  @ApiProperty({
    example: 1,
    description: 'User ID that owns or creates the meeting.',
  })
  @Type(() => Number)
  @IsInt()
  @Min(1)
  userId!: number;

  @ApiPropertyOptional({
    example: [2, 3],
    description: 'Optional list of existing user IDs invited as guests.',
    type: [Number],
  })
  @IsOptional()
  @Transform(({ value }) => toNumberArray(value))
  @IsArray()
  @IsInt({ each: true })
  @Min(1, { each: true })
  guestUserIds?: number[];

  @ApiPropertyOptional({
    example: ['guest@example.com', 'guest2@example.com'],
    description: 'Optional list of guest email addresses.',
    type: [String],
  })
  @IsOptional()
  @Transform(({ value }) => toStringArray(value))
  @IsArray()
  @IsEmail({}, { each: true })
  guestEmails?: string[];
}
