import {
  type BudgetLevel,
  type Duration,
  type Interest,
  type Pace,
  type Theme,
  type Transport,
  TRAVEL_BUDGET_LEVELS,
  TRAVEL_DURATIONS,
  TRAVEL_INTERESTS,
  TRAVEL_PACES,
  TRAVEL_THEMES,
  TRAVEL_TRANSPORTS,
  type TravelPreferences,
} from './types'

const DESTINATION = '深圳市光明区' as const

export type TravelValidationErrorCode = 'quick_route_unreachable'

export class TravelValidationError extends Error {
  constructor(message: string, readonly code?: TravelValidationErrorCode) {
    super(message)
    this.name = 'TravelValidationError'
  }
}

function isRecord(value: unknown): value is Record<string, unknown> {
  return typeof value === 'object' && value !== null && !Array.isArray(value)
}

function isEnumValue<T extends string>(value: unknown, allowed: readonly T[]): value is T {
  return typeof value === 'string' && allowed.includes(value as T)
}

function normalizeEnumArray<T extends string>(
  value: unknown,
  allowed: readonly T[],
  fieldName: string,
): T[] {
  if (!Array.isArray(value) || value.some(item => !isEnumValue(item, allowed)))
    throw new TravelValidationError(`${fieldName}包含无效选项`)

  const selected = new Set(value as T[])
  return allowed.filter(item => selected.has(item))
}

function normalizeInteger(value: unknown, fieldName: string, min: number, max: number): number {
  if (!Number.isInteger(value) || (value as number) < min || (value as number) > max)
    throw new TravelValidationError(`${fieldName}必须是 ${min} 至 ${max} 的整数`)
  return value as number
}

export function normalizeTravelPreferences(value: unknown): TravelPreferences {
  if (!isRecord(value))
    throw new TravelValidationError('出行偏好格式无效')
  if (value.destination !== DESTINATION)
    throw new TravelValidationError('当前仅支持深圳市光明区')

  const themes = normalizeEnumArray<Theme>(value.themes, TRAVEL_THEMES, '出行主题')
  const interests = normalizeEnumArray<Interest>(value.interests, TRAVEL_INTERESTS, '特色偏好')
  if (themes.length === 0 && interests.length === 0)
    throw new TravelValidationError('主题或特色偏好至少选择一项')
  if (!isEnumValue<Duration>(value.duration, TRAVEL_DURATIONS))
    throw new TravelValidationError('游玩时长无效')
  if (!isEnumValue<Pace>(value.pace, TRAVEL_PACES))
    throw new TravelValidationError('行程节奏无效')
  if (!isEnumValue<Transport>(value.transport, TRAVEL_TRANSPORTS))
    throw new TravelValidationError('交通方式无效')
  if (!isEnumValue<BudgetLevel>(value.budgetLevel, TRAVEL_BUDGET_LEVELS))
    throw new TravelValidationError('预算档位无效')

  const adults = normalizeInteger(value.adults, '成人数量', 0, 20)
  const children = normalizeInteger(value.children, '儿童数量', 0, 20)
  if (adults + children < 1)
    throw new TravelValidationError('出行总人数至少为 1')
  if (!Array.isArray(value.childAges) || value.childAges.length !== children)
    throw new TravelValidationError('儿童年龄数量必须与儿童人数一致')
  const childAges = value.childAges.map(age => normalizeInteger(age, '儿童年龄', 0, 17))

  if (typeof value.extraRequirements !== 'string')
    throw new TravelValidationError('补充要求格式无效')
  const extraRequirements = value.extraRequirements.trim()
  if (extraRequirements.length > 500)
    throw new TravelValidationError('补充要求不能超过 500 字')

  return {
    destination: DESTINATION,
    themes,
    duration: value.duration,
    pace: value.pace,
    interests,
    adults,
    children,
    childAges,
    transport: value.transport,
    budgetLevel: value.budgetLevel,
    extraRequirements,
  }
}

export function isTravelPreferences(value: unknown): value is TravelPreferences {
  try {
    normalizeTravelPreferences(value)
    return true
  }
  catch {
    return false
  }
}

export function cloneTravelPreferences(preferences: TravelPreferences): TravelPreferences {
  const normalized = normalizeTravelPreferences(preferences)
  return {
    ...normalized,
    themes: [...normalized.themes],
    interests: [...normalized.interests],
    childAges: [...normalized.childAges],
  }
}
