import { useState, useEffect, useRef } from 'react';
import { RefreshCw, Play, Square, Loader2 } from 'lucide-react';
import { Label } from '@/components/ui/label';
import {
  Select,
  SelectContent,
  SelectGroup,
  SelectItem,
  SelectLabel,
  SelectTrigger,
  SelectValue,
} from '@/components/ui/select';
import { useClient } from 'hooks/useClient';
import { useSettings } from 'state/useSettings';

type VoiceGroup = { label: string; voices: string[] };

type TtsConfig = {
  provider: string;
  url: string;
  apiKey?: string;
  model: string;
  voice: string;
};

const SERVER_DEFAULT = '__server_default__';

export const VoicePreference = () => {
  const client = useClient();
  const { settings, saveSettings } = useSettings();
  const [voices, setVoices] = useState<string[]>([]);
  const [groups, setGroups] = useState<VoiceGroup[]>([]);
  const [loading, setLoading] = useState(false);
  const [serverConfig, setServerConfig] = useState<TtsConfig | null>(null);
  const [listening, setListening] = useState<'idle' | 'loading' | 'playing'>('idle');
  const audioRef = useRef<HTMLAudioElement | null>(null);

  const fetchConfig = async () => {
    try {
      const config = await client.get<TtsConfig | null>('/server-settings/tts');
      setServerConfig(config);
      return config;
    } catch {
      return null;
    }
  };

  const fetchVoices = async (config: TtsConfig) => {
    setLoading(true);
    try {
      const res = await client.post<{ voices?: string[]; groups?: VoiceGroup[] }>('/server-settings/tts/voices', {
        provider: config.provider,
        url: config.url,
        apiKey: config.apiKey || undefined,
        model: config.model || undefined,
      });
      setVoices(res.voices ?? []);
      setGroups(res.groups ?? []);
    } catch {
      setVoices([]);
      setGroups([]);
    } finally {
      setLoading(false);
    }
  };

  useEffect(() => {
    fetchConfig().then((config) => {
      if (config) fetchVoices(config);
    });
  }, []);

  const handleChange = (value: string) => {
    const voice = value === SERVER_DEFAULT ? null : value;
    saveSettings({ ...settings, tts: { voice } });
  };

  const handleRefresh = () => {
    if (serverConfig) fetchVoices(serverConfig);
  };

  const handleListen = async () => {
    if (listening === 'loading') return;
    if (listening === 'playing') {
      audioRef.current?.pause();
      audioRef.current = null;
      setListening('idle');
      return;
    }
    if (!serverConfig) return;
    setListening('loading');
    try {
      const effectiveVoice = settings.tts.voice ?? serverConfig.voice;
      const res = await fetch(`${client.baseUrl}/server-settings/tts/test`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${client.token}` },
        body: JSON.stringify({ ...serverConfig, voice: effectiveVoice }),
      });
      if (!res.ok || res.headers.get('content-type')?.includes('json')) {
        setListening('idle');
        return;
      }
      const blob = await res.blob();
      const url = URL.createObjectURL(blob);
      const audio = new Audio(url);
      audioRef.current = audio;
      audio.onended = () => {
        audioRef.current = null;
        setListening('idle');
        URL.revokeObjectURL(url);
      };
      audio.onerror = () => {
        audioRef.current = null;
        setListening('idle');
        URL.revokeObjectURL(url);
      };
      await audio.play();
      setListening('playing');
    } catch {
      setListening('idle');
    }
  };

  const selectedValue = settings.tts.voice ?? SERVER_DEFAULT;
  const prettify = (v: string) => v.replace(/^[a-z]{2}_/, '').replace(/^\w/, (c) => c.toUpperCase());

  return (
    <div className="grid gap-4">
      <Label className="grid gap-2">
        <div className="flex items-center gap-1.5">
          <span className="text-duck-dark/70 dark:text-foreground/70">Voice</span>
          <button
            type="button"
            onClick={handleRefresh}
            disabled={loading}
            className="p-0.5 rounded hover:bg-duck-dark/10 dark:hover:bg-foreground/10 cursor-pointer transition-colors disabled:opacity-40"
            title="Refresh voices"
          >
            <RefreshCw
              className={`h-3 w-3 text-duck-dark/50 dark:text-foreground/50 ${loading ? 'animate-spin' : ''}`}
            />
          </button>
        </div>
        <div className="flex items-center gap-2">
          <Select value={selectedValue} onValueChange={handleChange}>
            <SelectTrigger className="h-11 bg-background/60 border-duck-dark/20 text-duck-dark dark:text-foreground">
              <SelectValue placeholder="Server default" />
            </SelectTrigger>
            <SelectContent className="z-[600] max-h-[300px]">
              <SelectItem value={SERVER_DEFAULT}>
                Server default{serverConfig?.voice ? ` (${prettify(serverConfig.voice)})` : ''}
              </SelectItem>
              {groups.length > 0
                ? groups.map((g) => (
                    <SelectGroup key={g.label}>
                      <SelectLabel className="text-xs font-semibold text-duck-dark/50 dark:text-foreground/50">
                        {g.label}
                      </SelectLabel>
                      {g.voices.map((v) => (
                        <SelectItem key={v} value={v}>
                          {prettify(v)}
                        </SelectItem>
                      ))}
                    </SelectGroup>
                  ))
                : voices.map((v) => (
                    <SelectItem key={v} value={v}>
                      {v}
                    </SelectItem>
                  ))}
            </SelectContent>
          </Select>
          <button
            type="button"
            onClick={handleListen}
            disabled={listening === 'loading' || !serverConfig}
            className="shrink-0 h-11 w-11 flex items-center justify-center rounded-md border border-duck-dark/20 bg-background/60 hover:bg-duck-dark/10 dark:hover:bg-foreground/10 cursor-pointer transition-colors disabled:opacity-40 disabled:cursor-default"
            title={listening === 'playing' ? 'Stop' : 'Listen'}
          >
            {listening === 'loading' ? (
              <Loader2 className="h-4 w-4 text-duck-dark/70 dark:text-foreground/70 animate-spin" />
            ) : listening === 'playing' ? (
              <Square className="h-4 w-4 text-duck-dark/70 dark:text-foreground/70" />
            ) : (
              <Play className="h-4 w-4 text-duck-dark/70 dark:text-foreground/70" />
            )}
          </button>
        </div>
        <span className="text-xs text-duck-dark/40 dark:text-foreground/40">
          Choose a voice for text-to-speech. Leave as server default to use the admin-configured voice.
        </span>
      </Label>
    </div>
  );
};
