import { useEffect, useState } from 'preact/hooks'; import type { MediaType } from '../types/index.js'; import './Modal.css'; type GenerationMode = 'brainstorm' | 'continuous'; interface NewRecommendationModalProps { onClose: () => void; onSubmit: (body: { main_prompt: string; liked_shows: string; disliked_shows: string; themes: string; requirements?: string; avoid?: string; brainstorm_count?: number; media_type: MediaType; use_web_search?: boolean; use_validator?: boolean; hard_requirements?: boolean; self_expansive?: boolean; expansive_passes?: number; expansive_mode?: 'soft' | 'extreme'; generation_mode?: GenerationMode; total_count?: number; validate_results?: boolean; }) => Promise; } const MEDIA_OPTIONS: Array<{ type: MediaType; icon: string; label: string; description: string; }> = [ { type: 'tv_show', icon: '📺', label: 'TV Shows', description: 'Serialized stories, limited series, and long-form comfort watches.', }, { type: 'movie', icon: '🎬', label: 'Movies', description: 'Feature films, prestige cinema, and one-night picks.', }, ]; const MODE_OPTIONS: Array<{ mode: GenerationMode; label: string; badge: string; description: string; }> = [ { mode: 'brainstorm', label: 'Brainstorm', badge: 'Best for variety', description: 'Explore a broad pool of options, then rank and curate the strongest fits.', }, { mode: 'continuous', label: 'Continuous', badge: 'Best for deep search', description: 'Generate recommendations in chained batches for a steadier, longer-running hunt.', }, ]; export function NewRecommendationModal({ onClose, onSubmit }: NewRecommendationModalProps) { const [step, setStep] = useState<'type' | 'mode' | 'form'>('type'); const [mediaType, setMediaType] = useState('tv_show'); const [generationMode, setGenerationMode] = useState('brainstorm'); const [mainPrompt, setMainPrompt] = useState(''); const [likedShows, setLikedShows] = useState(''); const [dislikedShows, setDislikedShows] = useState(''); const [themes, setThemes] = useState(''); const [requirements, setRequirements] = useState(''); const [avoid, setAvoid] = useState(''); const [brainstormCount, setBrainstormCount] = useState(100); const [totalCount, setTotalCount] = useState(30); const [useWebSearch, setUseWebSearch] = useState(false); const [useValidator, setUseValidator] = useState(false); const [useHardRequirements, setUseHardRequirements] = useState(false); const [selfExpansive, setSelfExpansive] = useState(false); const [expansivePasses, setExpansivePasses] = useState(2); const [expansiveMode, setExpansiveMode] = useState<'soft' | 'extreme'>('soft'); const [validateResults, setValidateResults] = useState(false); const [loading, setLoading] = useState(false); useEffect(() => { const handleKeyDown = (event: KeyboardEvent) => { if (event.key === 'Escape' && !loading) { onClose(); } }; window.addEventListener('keydown', handleKeyDown); return () => window.removeEventListener('keydown', handleKeyDown); }, [loading, onClose]); const mediaLabel = mediaType === 'movie' ? 'Movie' : 'TV Show'; const mediaPluralLabel = mediaType === 'movie' ? 'movies' : 'shows'; const handleSelectType = (type: MediaType) => { setMediaType(type); setStep('mode'); }; const handleWebSearchToggle = () => { const next = !useWebSearch; setUseWebSearch(next); if (!next) { setUseValidator(false); setValidateResults(false); } }; const handleSubmit = async (e: Event) => { e.preventDefault(); if (generationMode === 'brainstorm' && !mainPrompt.trim()) return; if (!likedShows.trim()) return; setLoading(true); try { if (generationMode === 'brainstorm') { await onSubmit({ main_prompt: mainPrompt.trim(), liked_shows: likedShows.trim(), disliked_shows: dislikedShows.trim(), themes: themes.trim(), brainstorm_count: brainstormCount, media_type: mediaType, use_web_search: useWebSearch, use_validator: useValidator, hard_requirements: useHardRequirements, self_expansive: selfExpansive, expansive_passes: selfExpansive ? expansivePasses : 1, expansive_mode: expansiveMode, generation_mode: 'brainstorm', }); } else { await onSubmit({ main_prompt: '', liked_shows: likedShows.trim(), disliked_shows: dislikedShows.trim(), themes: themes.trim(), requirements: requirements.trim(), avoid: avoid.trim(), media_type: mediaType, use_web_search: useWebSearch, validate_results: validateResults, generation_mode: 'continuous', total_count: totalCount, }); } onClose(); } finally { setLoading(false); } }; const handleBackdropClick = (e: MouseEvent) => { if ((e.target as HTMLElement).classList.contains('modal-backdrop') && !loading) { onClose(); } }; const selectedMode = MODE_OPTIONS.find((option) => option.mode === generationMode); return (