const App = () => {
  // React state hooks
  const [prompt, setPrompt] = React.useState('');
  const [result, setResult] = React.useState(null);
  const [loading, setLoading] = React.useState(false);
  const [activeTab, setActiveTab] = React.useState('text');
  const [imageUrl, setImageUrl] = React.useState('');
  const [uploadedImages, setUploadedImages] = React.useState([]);
  const [isListening, setIsListening] = React.useState(false);
  const [speechLanguage, setSpeechLanguage] = React.useState('en-US');
  const [speechOutputText, setSpeechOutputText] = React.useState('');
  const [speechMethod, setSpeechMethod] = React.useState('pollinations');
  const [audioRecorder, setAudioRecorder] = React.useState(null);
  const [audioChunks, setAudioChunks] = React.useState([]);
  const [isStreaming, setIsStreaming] = React.useState(true);
  const [streamedResult, setStreamedResult] = React.useState('');
  const [audioPlayerRef, setAudioPlayerRef] = React.useState(null);
  const [audioURL, setAudioURL] = React.useState(null);
  const [voiceOption, setVoiceOption] = React.useState('alloy');
  const [voiceToAudio, setVoiceToAudio] = React.useState(false);
  const [useAudioInput, setUseAudioInput] = React.useState(true);
  const [systemPrompt, setSystemPrompt] = React.useState('You are a helpful assistant.');
  const [showMarkdown, setShowMarkdown] = React.useState(false);
  const [apiKey, setApiKey] = React.useState(() => {
    return (window.POLLINATIONS_CONFIG && window.POLLINATIONS_CONFIG.API_KEY) || '';
  });
  const [ttsFormat, setTtsFormat] = React.useState('mp3');
  const [ttsSpeed, setTtsSpeed] = React.useState(1);
  const [ttsModel, setTtsModel] = React.useState('tts-1');

  // Custom hooks
  const { models, model, setModel, lastSelectedModels, setLastSelectedModels, getFilteredModels } =
    window.PollinationsHooks.useModelSelection({ activeTab, apiKey, voiceToAudio, setVoiceToAudio });

  const { enableMemory, chatHistory, setChatHistory, handleMemoryToggle } =
    window.PollinationsHooks.useChatHistory();
  const outputText = React.useRef('');

  let combinedLine = '';

  const audioContextRef = React.useRef(null);
  const workletNodeRef = React.useRef(null);

  // Audio worklet functions from module
  const restartAudioWorklet = () => window.PollinationsAudioWorklet.restartAudioWorklet(audioContextRef, workletNodeRef);
  const enqueueAudioData = window.PollinationsAudioWorklet.enqueueAudioData;

  React.useEffect(() => {
    restartAudioWorklet();
  }, []);

  const startAudioRecording = async () => {
    try {
      const stream = await navigator.mediaDevices.getUserMedia({
        audio: true,
        video: false
      });
      const audioContext = new(window.AudioContext || window.webkitAudioContext)({
        sampleRate: 44100
      });
      const mediaRecorder = new MediaRecorder(stream, {
        mimeType: MediaRecorder.isTypeSupported('audio/webm') ? 'audio/webm' : 'audio/mp4'
      });

      const chunks = [];
      mediaRecorder.ondataavailable = (e) => {
        chunks.push(e.data);
      };

      mediaRecorder.onstop = async () => {
        const audioBlob = new Blob(chunks, {
          type: 'audio/webm'
        });

        const arrayBuffer = await audioBlob.arrayBuffer();
        const audioBuffer = await audioContext.decodeAudioData(arrayBuffer);

        const wavBlob = await convertToWav(audioBuffer);
        const base64str = await blobToBase64(wavBlob);

        window.recordedAudioBase64 = base64str;

        startSpeechRecognition();
      };

      mediaRecorder.start();
      setAudioRecorder(mediaRecorder);
      setAudioChunks([]);
      setIsListening(true);
    } catch (error) {
      console.error('Error starting audio recording:', error);
      if (error.name === 'NotAllowedError' || error.name === 'PermissionDeniedError') {
        alert('Microphone permission was denied. Please allow microphone access to use this feature.');
      } else if (error.name === 'NotFoundError') {
        alert('No microphone found. Please ensure your device has a working microphone.');
      } else if (error.name === 'NotSupportedError') {
        alert('Audio recording is not supported on this browser or device.');
      } else {
        alert('Could not start audio recording: ' + error.message);
      }
    }
  };

  // Audio utility functions from module
  const convertToWav = window.PollinationsAudioUtils.convertToWav;
  const blobToBase64 = window.PollinationsAudioUtils.blobToBase64;
  const bufferToWav = window.PollinationsAudioUtils.bufferToWav;
  const convertPCM16ToFloat32 = (pcmData, originalSampleRate) => {
    const targetRate = audioContextRef.current ? audioContextRef.current.sampleRate : 44100;
    return window.PollinationsAudioUtils.convertPCM16ToFloat32(pcmData, originalSampleRate, targetRate);
  };

  const stopAudioRecording = () => {
    if (audioRecorder) {
      audioRecorder.stop();
      setIsListening(false);
    }
  };

  const startSpeechRecognition = async () => {
    if (speechMethod === 'webkit') {
      const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
      if (!SpeechRecognition) {
        console.error('Speech recognition not supported');
        return null;
      }
      const recognitionInstance = new SpeechRecognition();
      recognitionInstance.continuous = false;
      recognitionInstance.interimResults = false;
      recognitionInstance.lang = speechLanguage;

      recognitionInstance.onresult = (event) => {
        setIsListening(false);
        const transcript = event.results[0][0].transcript;
        setPrompt(transcript);
      };

      recognitionInstance.onerror = (event) => {
        console.error('Speech recognition error:', event.error);
        setIsListening(false);
      };

      recognitionInstance.onend = () => {
        setIsListening(false);
      };

      setIsListening(true);
      recognitionInstance.start();
    } else {
      setLoading(true);

      try {
        if (!window.recordedAudioBase64) {
          console.error('No recorded audio found');
          alert('Please record audio first');
          setLoading(false);
          return;
        }



        await generateAudio();
      } catch (error) {
        console.error('Detailed error with Pollinations speech-to-text:', error);
        setSpeechOutputText('Error processing speech-to-text');
      } finally {
        setLoading(false);
        setIsListening(false);
      }
    }
  };

  const stopSpeechRecognition = () => {
    if (speechMethod === 'webkit' && window.recognition) {
      window.recognition.stop();
    }
    setIsListening(false);
  };

  const sendRequest = () => {
    generateAudio();
  };

  const handleSendTextOnlyAudioRequest = () => {
    window.recordedAudioBase64 = null;
    generateAudio();
  }

  const generateTTS = async () => {
    setLoading(true);
    setAudioURL(null);
    setResult('');

    try {
      // Use refactored API service
      const audioBlob = await window.PollinationsAPI.generateSpeech({
        apiKey,
        model: ttsModel,
        input: prompt,
        voice: voiceOption,
        responseFormat: ttsFormat,
        speed: ttsSpeed
      });
      const audioUrl = URL.createObjectURL(audioBlob);
      setAudioURL(audioUrl);
      setResult(`✅ Audio generated successfully! Format: ${ttsFormat}, Voice: ${voiceOption}, Speed: ${ttsSpeed}x`);
    } catch (error) {
      console.error('Error generating TTS:', error);
      setResult('Error generating audio: ' + error.message);
    }
    setLoading(false);
  };

  const combinedChunk = (chunk) => {
    let completedSSE = '';
    combinedLine = combinedLine + chunk;
    while (combinedLine.length > 0 && combinedLine.indexOf("\n\n") > 0) { // SSE end
      const index = combinedLine.indexOf("\n\n");
      completedSSE = completedSSE + combinedLine.slice(0, index) + '\n';
      combinedLine = combinedLine.slice(index + 2);
      continue;
    }
    return completedSSE;
  }

  const handleImageUpload = (event) => {
    const files = event.target.files;
    if (!files || files.length === 0) return;

    for (let i = 0; i < files.length; i++) {
      const file = files[i];
      const reader = new FileReader();

      reader.onload = (e) => {
        setUploadedImages(prev => [...prev, {
          id: Date.now() + i,
          name: file.name,
          dataUrl: e.target.result
        }]);
      };

      reader.readAsDataURL(file);
    }
  };

  const handleImagePaste = (event) => {
    const items = (event.clipboardData || event.originalEvent.clipboardData).items;

    for (const item of items) {
      if (item.type.indexOf('image') === 0) {
        const blob = item.getAsFile();
        const reader = new FileReader();

        reader.onload = (e) => {
          setUploadedImages(prev => [...prev, {
            id: Date.now(),
            name: 'Pasted image',
            dataUrl: e.target.result
          }]);
        };

        reader.readAsDataURL(blob);
      }
    }
  };

  const removeImage = (id) => {
    setUploadedImages(prev => prev.filter(img => img.id !== id));
  };

  const generateAudio = async () => {

    const seed = Math.floor(Math.random() * 65535);

    // Determine if there is audio input
    const inputAudio = useAudioInput && window.recordedAudioBase64 != null && (activeTab === 'speech');
    const outputAudio = voiceToAudio;

    setOutput('');
    setLoading(true);
    setAudioChunks([]);
    if (outputAudio) {
        setAudioURL(null);
    }

    try {
      // Create messages with history if memory is enabled
      let messages = [];

      // Create content array based on active tab
      let content = [{
        type: "text",
        text: prompt
      }];

      if (activeTab === 'vision') {
        // Add main image URL if provided
        if (imageUrl) {
          content.push({
            type: "image_url",
            image_url: {
              url: imageUrl
            }
          });
        }
        // Add all uploaded images
        uploadedImages.forEach(img => {
          content.push({
            type: "image_url",
            image_url: {
              url: img.dataUrl
            }
          });
        });
      }

      if (inputAudio) {
        content.push({
          type: "input_audio",
          input_audio: {
            data: window.recordedAudioBase64,
            format: "wav"
          }
        });
      }

      let newMessage = {
        role: "user",
        content: content
      }
      if (enableMemory && chatHistory.length > 0) {
        messages = [...chatHistory, newMessage];
      } else {
        messages = [newMessage];
      }

      // Determine modalities based on the model's capabilities from API
      const currentModelInfo = models.find(m => m.name === model);
      const modelOutputModalities = currentModelInfo && currentModelInfo.output_modalities
        ? currentModelInfo.output_modalities
        : ['text'];
      const modelRequiresAudio = modelOutputModalities.includes('audio');

      // Build completion options
      const completionOptions = {
        apiKey,
        model,
        messages,
        systemPrompt,
        stream: isStreaming,
        seed
      };

      // If the model supports audio output or user requests audio output,
      // set modalities accordingly
      if (modelRequiresAudio || outputAudio) {
        completionOptions.modalities = modelOutputModalities;
        completionOptions.audio = {
          voice: voiceOption,
          format: "pcm16"
        };
      }

      // Use refactored API service
      const response = await window.PollinationsAPI.generateCompletion(completionOptions);



      if (outputAudio) {
        await restartAudioWorklet();
      }

      combinedLine = '';
      let localAudioChunks = [];
      // Read the stream
      const useStreaming = isStreaming;
      if (!useStreaming) {
        if (outputAudio) {
          const reader = response.body.getReader();
          const decoder = new TextDecoder();

          let totalBuffer = [];
          while (true) {
            const {
              done,
              value
            } = await reader.read();

            if (!value) break;
            if (done) break;
            // append audio bytes
            totalBuffer = [...totalBuffer, ...value];
          }

          const audioBytes = new Uint8Array(totalBuffer);

          // Add to audio chunks
          setAudioChunks(prevChunks => [...prevChunks, audioBytes]);
          localAudioChunks = [...localAudioChunks, audioBytes];

          // Send to AudioWorklet for real-time playback
          const floatData = convertPCM16ToFloat32(audioBytes, 24000);
          enqueueAudioData(workletNodeRef.current, floatData);
        } else {
          const rawData = await response.text();
          try {
            const parsedData = JSON.parse(rawData);
            if (parsedData.choices && parsedData.choices[0] && parsedData.choices[0].message) {
              // Check for reasoning_content if content is null
              setOutput(parsedData.choices[0].message.content || parsedData.choices[0].message.reasoning_content);
            } else {
              setOutput(JSON.stringify(parsedData, null, 2)); // Fallback to raw JSON if structure is unexpected
            }
          } catch (jsonError) {
            // If not JSON, use the raw text
            setOutput(rawData);
          }
        }
      } else {
        // Streaming
        const reader = response.body.getReader();
        const decoder = new TextDecoder();
        while (true) {
          const {
            done,
            value
          } = await reader.read();
          if (done) break;

          const chunk = decoder.decode(value, {
            stream: isStreaming
          });


          // Combine chunks
          let completedSSE = combinedChunk(chunk);



          // Process SSE format
          const lines = completedSSE.split("\n").filter(line => line.startsWith("data: "));

          for (const line of lines) {
            try {
              const jsonString = line.replace("data: ", "").trim();


              if (jsonString === "[DONE]") continue;

              const parsedChunk = JSON.parse(jsonString);


              if (parsedChunk && parsedChunk.choices && parsedChunk.choices[0] && parsedChunk.choices[0].delta && parsedChunk.choices[0].delta.audio) {

                // Handle audio data from delta format
                const audioData = parsedChunk.choices[0].delta.audio.data;
                if (audioData) {
                  // Decode base64 audio chunk
                  const binaryData = atob(audioData);
                  const arrayBuffer = new ArrayBuffer(binaryData.length);
                  const audioBytes = new Uint8Array(arrayBuffer);
                  for (let i = 0; i < binaryData.length; i++) {
                    audioBytes[i] = binaryData.charCodeAt(i);
                  }

                  // Add to audio chunks
                  setAudioChunks(prevChunks => [...prevChunks, audioBytes]);
                  localAudioChunks = [...localAudioChunks, audioBytes];

                  // Send to AudioWorklet for real-time playback
                  const floatData = convertPCM16ToFloat32(audioBytes, 24000);
                  enqueueAudioData(workletNodeRef.current, floatData);
                }

                // Update text result if transcript is available
                if (parsedChunk.choices[0].delta.audio.transcript) {
                  appendOutput(parsedChunk.choices[0].delta.audio.transcript);
                }
              } else if (parsedChunk && parsedChunk.choices && parsedChunk.choices[0] && parsedChunk.choices[0].delta) {
                // Also check for reasoning_content if content is null
                const content = parsedChunk.choices[0].delta.content || parsedChunk.choices[0].delta.reasoning_content || "";
                if (content) {
                  appendOutput(content);
                }
              } else if (parsedChunk && parsedChunk.audio) {
                // Handle legacy format for backward compatibility
                const audioData = `data:audio/mp3;base64,${parsedChunk.audio}`;
                appendOutput(`<audio controls src="${audioData}"></audio>`);
                setAudioURL(audioData);

                // Update text result if available
                if (parsedChunk.text) {
                  appendOutput(parsedChunk.text);
                }
              }
            } catch (error) {
              console.error("JSON parsing error:", error);

            }
          }
        } // end while
      }
      // Combine audio chunks into WAV file for download/playback
      if (outputAudio && localAudioChunks.length > 0) {
        let combinedChunks = new Uint8Array(localAudioChunks.reduce((acc, chunk) => acc + chunk.length, 0));
        let offset = 0;
        for (const chunk of localAudioChunks) {
          combinedChunks.set(chunk, offset);
          offset += chunk.length;
        }

        const wavData = bufferToWav(combinedChunks);
        const audioBlob = new Blob([wavData], { type: 'audio/wav' });
        setAudioURL(URL.createObjectURL(audioBlob));
      }

      // Stop AudioWorklet playback
      if (outputAudio) {
        window.PollinationsAudioWorklet.stopAudioWorklet(workletNodeRef);
      }
    } catch (error) {
      console.error('Error generating audio:', error);
      setOutput('Error generating audio: ' + error.message);
    }
    setLoading(false);

    // Save to chat history if memory is enabled
    if (enableMemory) {
      setChatHistory(prev => [
        ...prev, {
          role: 'user',
          content: prompt
        }, {
          role: 'assistant',
          content: getOutput()
        }
      ]);
    }
  };

  const setOutput = (msg) => {
    outputText.current = msg;
    if (isStreaming)
      setStreamedResult(msg);
    else
      setResult(msg);
  }
  const appendOutput = (msg) => {
    outputText.current = outputText.current + msg;
    if (isStreaming)
      setStreamedResult(prev => prev + msg);
    else
      setResult(prev => prev + msg);
  }
  const getOutput = () => {
    return outputText.current;
  }

  return (
    <div className="container mx-auto p-6" onPaste={handleImagePaste}>
      <h1 className="text-3xl font-bold mb-6 text-center">Pollinations AI API (New gen.pollinations.ai)</h1>
      <div className="bg-green-100 border border-green-400 text-green-700 px-4 py-3 rounded mb-4 text-center">
        ✅ Now using the new API at <strong>gen.pollinations.ai</strong>
      </div>

      <div className="flex justify-center mb-4">
        <button
          className={`px-4 py-2 mx-2 ${activeTab === 'text' ? 'bg-blue-500 text-white' : 'bg-gray-200'}`}
          onClick={() => setActiveTab('text')}
        >
          Text Generation
        </button>
        <button
          className={`px-4 py-2 mx-2 ${activeTab === 'vision' ? 'bg-blue-500 text-white' : 'bg-gray-200'}`}
          onClick={() => setActiveTab('vision')}
        >
          Vision
        </button>
        <button
          className={`px-4 py-2 mx-2 ${activeTab === 'tts' ? 'bg-blue-500 text-white' : 'bg-gray-200'}`}
          onClick={() => setActiveTab('tts')}
        >
          Text-to-Speech
        </button>
        <button
          className={`px-4 py-2 mx-2 ${activeTab === 'speech' ? 'bg-blue-500 text-white' : 'bg-gray-200'}`}
          onClick={() => setActiveTab('speech')}
        >
          Speech-to-Text
        </button>
      </div>

      <div className="bg-white shadow-md rounded px-8 pt-6 pb-8 mb-4">

        <div className="mb-4 p-4 bg-blue-50 border border-blue-200 rounded">
          <label className="block text-gray-700 text-sm font-bold mb-2">
            API Key (Optional)
          </label>
          <input
            type="password"
            value={apiKey}
            onChange={(e) => setApiKey(e.target.value)}
            placeholder="Enter your Pollinations API key (get it at enter.pollinations.ai)"
            className="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"
          />
          <p className="text-sm text-gray-600 mt-2">
            ℹ️ Anonymous requests still work, but an API key gives you better performance and access to all models.
            Get your key at <a href="https://enter.pollinations.ai" target="_blank" className="text-blue-600 underline">enter.pollinations.ai</a>
          </p>
        </div>

        <div className="mb-4">
          <label className="block text-gray-700 text-sm font-bold mb-2">
            System Prompt
          </label>
          <input
            type="text"
            value={systemPrompt}
            onChange={(e) => setSystemPrompt(e.target.value)}
            placeholder="Enter system prompt"
            className="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"
          />
        </div>

        <div className="mb-4">
          <label className="block text-gray-700 text-sm font-bold mb-2">
            Prompt
          </label>
          <input
            type="text"
            value={prompt}
            onChange={(e) => setPrompt(e.target.value)}
            placeholder="Enter your prompt"
            className="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"
          />
        </div>

        <div className="mb-4">
          <label className="block text-gray-700 text-sm font-bold mb-2">
            Model
          </label>
          <select
            value={model}
            onChange={(e) => {
              const newModelName = e.target.value;
              const targetModel = models.find((modelOption) => modelOption.name === newModelName);
              if (targetModel) {
                setModel(targetModel.name);
                setLastSelectedModels(prev => ({ ...prev, [activeTab]: targetModel.name }));
              }
            }}
            className="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"
            style={{
              maxWidth: '100%',
              textOverflow: 'ellipsis',
              whiteSpace: 'nowrap',
              overflow: 'hidden'
            }}
          >
            {getFilteredModels().map((modelOption) => (
              <option
                key={modelOption.id}
                value={modelOption.name}
                title={modelOption.name}
                className="text-base"
              >
                {modelOption.name + " - " + modelOption.description}
              </option>
            ))}
          </select>
        </div>

        <div className="mb-4 flex items-center">
          <label className="mr-4">
            <input
              type="checkbox"
              checked={isStreaming}
              onChange={() => setIsStreaming(!isStreaming)}
              className="mr-2"
            />
            Enable Streaming
          </label>
          <label className="mr-4">
            <input
              type="checkbox"
              checked={enableMemory}
              onChange={handleMemoryToggle}
              className="mr-2"
            />
            Enable Chat Memory
          </label>
          <label className="mr-4">
            <input
              type="checkbox"
              checked={showMarkdown}
              onChange={() => setShowMarkdown(!showMarkdown)}
              className="mr-2"
            />
            Render Markdown
          </label>
        </div>

        <div className="mb-4 p-4 border rounded bg-gray-50">
            <div className="flex items-center">
                {(() => {
                  const currentModel = models.find(m => m.name === model);
                  const hasAudio = currentModel && currentModel.audio;
                  return (
                    <React.Fragment>
                      <label className={`inline-flex items-center mr-4 ${!hasAudio ? 'opacity-50 cursor-not-allowed' : ''}`}>
                          <input
                          type="checkbox"
                          checked={voiceToAudio}
                          disabled={!hasAudio}
                          onChange={() => setVoiceToAudio(!voiceToAudio)}
                          className="mr-2"
                          />
                          <span>Generate audio response</span>
                      </label>
                      {voiceToAudio && hasAudio && (
                    <div className="flex items-center">
                        <label className="block text-gray-700 text-sm font-bold mr-2">
                            Voice:
                        </label>
                        <select
                            value={voiceOption}
                            onChange={(e) => setVoiceOption(e.target.value)}
                            className="shadow-sm appearance-none border rounded py-1 px-2 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"
                        >
                            <option value="alloy">Alloy</option>
                            <option value="echo">Echo</option>
                            <option value="fable">Fable</option>
                            <option value="onyx">Onyx</option>
                            <option value="nova">Nova</option>
                            <option value="shimmer">Shimmer</option>
                            <option value="coral">Coral</option>
                            <option value="verse">Verse</option>
                            <option value="ballad">Ballad</option>
                            <option value="ash">Ash</option>
                            <option value="sage">Sage</option>
                            <option value="amuch">Amuch</option>
                            <option value="dan">Dan</option>
                        </select>
                    </div>
                      )}
                    </React.Fragment>
                  );
                })()}
            </div>
            {(() => {
              const currentModel = models.find(m => m.name === model);
              const hasAudio = currentModel && currentModel.audio;
              return !hasAudio ? (
                <p className="text-sm text-gray-600 mt-2 bg-blue-50 p-2 rounded">
                  ℹ️ Audio output is only available with audio-capable models like <strong>openai-audio</strong>.
                  Select an audio-capable model to enable this feature.
                </p>
              ) : null;
            })()}
        </div>

        {activeTab === 'text' && (
          <button
            onClick={sendRequest}
            disabled={loading}
            className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline"
          >
            {loading ? 'Generating...' : 'Generate'}
          </button>
        )}

        {activeTab === 'vision' && (
          <div>
            <input
              type="text"
              placeholder="Image URL (optional)"
              value={imageUrl}
              onChange={(e) => setImageUrl(e.target.value)}
              className="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline mb-4"
            />

            <div className="mb-4">
              <label className="block text-gray-700 text-sm font-bold mb-2">
                Upload Images
              </label>
              <input
                type="file"
                accept="image/*"
                multiple
                onChange={handleImageUpload}
                className="block w-full text-gray-700 py-2"
              />
              <div className="mt-2 text-sm text-gray-600">
                You can also paste images with Ctrl+V/Cmd+V
              </div>
            </div>

            {uploadedImages.length > 0 && (
              <div className="mb-4">
                <div className="flex flex-wrap gap-2">
                  {uploadedImages.map(img => (
                    <div key={img.id} className="relative">
                      <img
                        src={img.dataUrl}
                        alt={img.name}
                        className="h-24 w-auto object-cover border rounded"
                      />
                      <button
                        onClick={() => removeImage(img.id)}
                        className="absolute top-0 right-0 bg-red-500 text-white rounded-full w-6 h-6 flex items-center justify-center"
                      >
                        ×
                      </button>
                    </div>
                  ))}
                </div>
              </div>
            )}

            <button
              onClick={sendRequest}
              disabled={loading || (imageUrl === '' && uploadedImages.length === 0)}
              className="bg-purple-500 hover:bg-purple-700 text-white font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline"
            >
              {loading ? 'Analyzing...' : 'Analyze Images'}
            </button>
          </div>
        )}

        {activeTab === 'tts' && (
          <div>
            <div className="mb-4">
              <label className="block text-gray-700 text-sm font-bold mb-2">
                Voice
              </label>
              <select
                value={voiceOption}
                onChange={(e) => setVoiceOption(e.target.value)}
                className="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"
              >
                <option value="alloy">Alloy</option>
                <option value="echo">Echo</option>
                <option value="fable">Fable</option>
                <option value="onyx">Onyx</option>
                <option value="nova">Nova</option>
                <option value="shimmer">Shimmer</option>
                <option value="coral">Coral</option>
                <option value="verse">Verse</option>
                <option value="ballad">Ballad</option>
                <option value="ash">Ash</option>
                <option value="sage">Sage</option>
              </select>
            </div>

            <div className="mb-4">
              <label className="block text-gray-700 text-sm font-bold mb-2">
                Audio Format
              </label>
              <select
                value={ttsFormat}
                onChange={(e) => setTtsFormat(e.target.value)}
                className="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"
              >
                <option value="mp3">MP3</option>
                <option value="wav">WAV</option>
                <option value="opus">Opus</option>
                <option value="aac">AAC</option>
                <option value="flac">FLAC</option>
                <option value="pcm">PCM</option>
              </select>
            </div>

            <div className="mb-4">
              <label className="block text-gray-700 text-sm font-bold mb-2">
                Speed: {ttsSpeed}x
              </label>
              <input
                type="range"
                min="0.25"
                max="4"
                step="0.25"
                value={ttsSpeed}
                onChange={(e) => setTtsSpeed(parseFloat(e.target.value))}
                className="w-full"
              />
              <div className="flex justify-between text-xs text-gray-600">
                <span>0.25x</span>
                <span>4x</span>
              </div>
            </div>

            <div className="mb-4">
              <label className="block text-gray-700 text-sm font-bold mb-2">
                Model
              </label>
              <select
                value={ttsModel}
                onChange={(e) => setTtsModel(e.target.value)}
                className="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"
              >
                <option value="tts-1">TTS-1 (Standard)</option>
                <option value="tts-1-hd">TTS-1 HD (High Quality)</option>
              </select>
            </div>

            <button
              onClick={generateTTS}
              disabled={loading || !prompt}
              className="bg-green-500 hover:bg-green-700 text-white font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline"
            >
              {loading ? 'Generating Audio...' : 'Generate Speech'}
            </button>
          </div>
        )}

        {(activeTab === 'audio' || activeTab === 'speech') && (
          <div>
            <div className="mb-4">
              <label className="inline-flex items-center">
                <input
                  type="checkbox"
                  checked={useAudioInput}
                  onChange={() => setUseAudioInput(!useAudioInput)}
                  className="mr-2"
                />
                <span>Use audio input</span>
              </label>
            </div>

            {activeTab === 'audio' && (
              <button
                onClick={sendRequest}
                disabled={loading}
                className="bg-green-500 hover:bg-green-700 text-white font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline"
              >
                {loading ? 'Generating...' : 'Generate Audio'}
              </button>
            )}
          </div>
        )}

        {activeTab === 'speech' && (
          <div className="bg-white shadow-md rounded px-8 pt-6 pb-8 mb-4">
            {useAudioInput && (
            <div className="mb-4">
              <label className="block text-gray-700 text-sm font-bold mb-2">
                Speech Recognition Method
              </label>
              <div className="flex items-center">
                <label className="inline-flex items-center mr-4">
                  <input
                    type="radio"
                    value="webkit"
                    checked={speechMethod === 'webkit'}
                    onChange={() => setSpeechMethod('webkit')}
                    className="form-radio"
                  />
                  <span className="ml-2">WebKit (Browser Native)</span>
                </label>
                <label className="inline-flex items-center">
                  <input
                    type="radio"
                    value="pollinations"
                    checked={speechMethod === 'pollinations'}
                    onChange={() => setSpeechMethod('pollinations')}
                    className="form-radio"
                  />
                  <span className="ml-2">Pollinations.ai</span>
                </label>
              </div>
            </div>
            )}

            {speechMethod === 'webkit' && useAudioInput && (
              <div className="mb-4">
                <label className="block text-gray-700 text-sm font-bold mb-2">
                  Speech Language
                </label>
                <select
                  value={speechLanguage}
                  onChange={(e) => setSpeechLanguage(e.target.value)}
                  className="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"
                >
                  <option value="en-US">English (US)</option>
                  <option value="es-ES">Spanish (Spain)</option>
                  <option value="fr-FR">French (France)</option>
                  <option value="de-DE">German (Germany)</option>
                  <option value="it-IT">Italian (Italy)</option>
                  <option value="ja-JP">Japanese</option>
                  <option value="zh-CN">Chinese (Simplified)</option>
                  <option value="ar-SA">Arabic (Saudi Arabia)</option>
                  <option value="hi-IN">Hindi (India)</option>
                  <option value="pt-BR">Portuguese (Brazil)</option>
                </select>
              </div>
            )}

            {speechMethod === 'pollinations' && useAudioInput && (
              <div className="flex space-x-4 mb-4">
                {!isListening && (
                  <button
                    onClick={startAudioRecording}
                    disabled={isListening}
                    className="bg-green-500 hover:bg-green-700 text-white font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline"
                  >
                    {!loading ? 'Start Recording' : 'Generating...'}
                  </button>
                )}
                {isListening && (
                  <button
                    onClick={stopAudioRecording}
                    className="bg-red-500 hover:bg-red-700 text-white font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline"
                  >
                    Stop & Transcribe
                  </button>
                )}
              </div>
            )}
            {!useAudioInput && (
              <div className="flex space-x-4 mb-4">
                <button
                  onClick={handleSendTextOnlyAudioRequest}
                  disabled={loading}
                  className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline"
                >
                  {loading ? 'Generating...' : 'Send'}
                </button>
              </div>
            )}
            {speechMethod === 'webkit' && useAudioInput && (
              <div className="flex space-x-4 mb-4">
                <button
                  onClick={startSpeechRecognition}
                  disabled={isListening}
                  className="bg-green-500 hover:bg-green-700 text-white font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline"
                >
                  {isListening ? 'Listening...' : 'Start WebKit Speech Input'}
                </button>
                {isListening && (
                  <button
                    onClick={stopSpeechRecognition}
                    className="bg-red-500 hover:bg-red-700 text-white font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline"
                  >
                    Stop Listening
                  </button>
                )}
              </div>
            )}
          </div>
        )}

        {audioURL && (
          <div className="mt-4">
            <audio
              controls
              src={audioURL}
              ref={ref => setAudioPlayerRef(ref)}
              className="w-full"
            />
          </div>
        )}

        {result && !isStreaming && (
          <div className="mt-4 p-4 bg-gray-100 rounded">
            {result.startsWith('<audio') ? (
              <div dangerouslySetInnerHTML={{ __html: result }}></div>
            ) : showMarkdown ? (
              <div className="markdown-content" dangerouslySetInnerHTML={{ __html: marked.parse(result) }}></div>
            ) : (
              <pre className="whitespace-pre-wrap">{result}</pre>
            )}
          </div>
        )}

        {streamedResult && isStreaming && (
          <div className="mt-4 p-4 bg-gray-100 rounded">
            {showMarkdown ? (
              <div className="markdown-content" dangerouslySetInnerHTML={{ __html: marked.parse(streamedResult) }}></div>
            ) : (
              <pre className="whitespace-pre-wrap">{streamedResult}</pre>
            )}
          </div>
        )}
      </div>

      {enableMemory && (
        <div className="mt-6">
          <h2 className="text-xl font-semibold mb-4">Chat History</h2>
          <div className="bg-gray-100 p-4 rounded">
            {chatHistory.length > 0 ? (
              <ul className="list-disc list-inside">
                {chatHistory.map((entry, index) => (
                  <li key={index} className="mb-2">
                    <strong>({index}) {entry.role === 'user' ? 'User' : 'Assistant'}:</strong> {entry.content}
                  </li>
                ))}
              </ul>
            ) : (
              <p className="text-gray-600">No chat history available.</p>
            )}
          </div>
        </div>
      )}

      <div className="mt-6 text-center">
        <h2 className="text-xl font-semibold mb-4">API Capabilities</h2>
        <ul className="list-disc list-inside">
          <li>✅ Using new gen.pollinations.ai API</li>
          <li>Text Generation with multiple models (OpenAI-compatible)</li>
          <li>Image Analysis (Vision)</li>
          <li>Text-to-Speech with multiple voices</li>
          <li>Speech-to-Text Recognition</li>
          <li>Streaming support</li>
          <li>Chat memory</li>
          <li>Optional API key authentication for better performance</li>
        </ul>
        <div className="mt-4 p-4 bg-yellow-50 border border-yellow-200 rounded">
          <p className="text-sm">
            📝 <strong>Note:</strong> Get your API key at <a href="https://enter.pollinations.ai" target="_blank" className="text-blue-600 underline">enter.pollinations.ai</a> for access to all models and better performance.
          </p>
        </div>
      </div>
    </div>
  );
};

ReactDOM.render(<App />, document.getElementById('root'));
