/**
 * SynthesizeDto
 *
 * DTO for text-to-speech synthesis requests.
 * Text and option fields are validated by class-validator decorators.
 * Additional options control voice, speed, format, and tier selection.
 *
 * Issue #398
 */

import { IsString, IsOptional, IsNumber, IsIn, Min, Max, MaxLength } from "class-validator";
import { Type } from "class-transformer";
import { AUDIO_FORMATS, SPEECH_TIERS } from "../interfaces/speech-types";
import type { AudioFormat, SpeechTier } from "../interfaces/speech-types";

export class SynthesizeDto {
  /**
   * Text to convert to speech.
   * Validated by class-validator decorators for type and maximum length.
   */
  @IsString({ message: "text must be a string" })
  @MaxLength(4096, { message: "text must not exceed 4096 characters" })
  text!: string;

  /**
   * Voice ID to use for synthesis.
   * Available voices depend on the selected tier and provider.
   * If omitted, the default voice from speech config is used.
   */
  @IsOptional()
  @IsString({ message: "voice must be a string" })
  @MaxLength(100, { message: "voice must not exceed 100 characters" })
  voice?: string;

  /**
   * Speech speed multiplier (0.5 to 2.0).
   * 1.0 is normal speed, <1.0 is slower, >1.0 is faster.
   */
  @IsOptional()
  @Type(() => Number)
  @IsNumber({}, { message: "speed must be a number" })
  @Min(0.5, { message: "speed must be at least 0.5" })
  @Max(2.0, { message: "speed must not exceed 2.0" })
  speed?: number;

  /**
   * Desired audio output format.
   * Supported: mp3, wav, opus, flac, aac, pcm.
   * If omitted, the default format from speech config is used.
   */
  @IsOptional()
  @IsString({ message: "format must be a string" })
  @IsIn(AUDIO_FORMATS, {
    message: `format must be one of: ${AUDIO_FORMATS.join(", ")}`,
  })
  format?: AudioFormat;

  /**
   * TTS tier to use for synthesis.
   * Controls which provider is used: default (Kokoro), premium (Chatterbox), or fallback (Piper).
   * If the selected tier is unavailable, the service falls back to the next available tier.
   */
  @IsOptional()
  @IsString({ message: "tier must be a string" })
  @IsIn(SPEECH_TIERS, {
    message: `tier must be one of: ${SPEECH_TIERS.join(", ")}`,
  })
  tier?: SpeechTier;
}
