// --- MOCK API and DATA ---
// This section simulates the AI model's responses so the app can run without an API key.

const MOCK_DELAY = 800; // Simulate network delay in milliseconds

const mockData = {
  vocabulary: {
    text: JSON.stringify([
      { word: 'Algorithm', definition: 'A set of rules for solving a problem in a finite number of steps.', example: 'Search engines use a complex algorithm to rank pages.', pronunciation: 'ˈælɡərɪðəm', translation: 'Algoritma' },
      { word: 'Cloud Computing', definition: 'Using a network of remote servers hosted on the internet to store, manage, and process data.', example: 'We moved our data to the cloud for better accessibility.', pronunciation: 'klaʊd kəmˈpjuːtɪŋ', translation: 'Komputasi awan' },
      { word: 'Encryption', definition: 'The process of converting information into a code to prevent unauthorized access.', example: 'End-to-end encryption keeps your messages private.', pronunciation: 'ɪnˈkrɪpʃn', translation: 'Enkripsi' },
      { word: 'Firewall', definition: 'A network security system that monitors and controls incoming and outgoing network traffic.', example: 'The company\'s firewall blocked the malicious website.', pronunciation: 'ˈfaɪərwɔːl', translation: 'Dinding api' },
      { word: 'User Interface', definition: 'The point of human-computer interaction and communication in a device.', example: 'The application has a clean and intuitive user interface.', pronunciation: 'ˈjuːzər ˈɪntərfeɪs', translation: 'Antarmuka pengguna' },
      { word: 'Bandwidth', definition: 'The maximum rate of data transfer across a given path.', example: 'Streaming high-definition video requires a lot of bandwidth.', pronunciation: 'ˈbændwɪdθ', translation: 'Lebar pita' },
      { word: 'Database', definition: 'An organized collection of structured information, or data, typically stored electronically.', example: 'All customer information is stored in a secure database.', pronunciation: 'ˈdeɪtəbeɪs', translation: 'Basis data' },
      { word: 'Malware', definition: 'Software designed to disrupt, damage, or gain unauthorized access to a computer system.', example: 'Antivirus software helps protect against malware.', pronunciation: 'ˈmælweər', translation: 'Perangkat perusak' },
      { word: 'Software', definition: 'Programs and other operating information used by a computer.', example: 'You need to update your software to the latest version.', pronunciation: 'ˈsɒftweər',translation: 'Perangkat lunak' },
      { word: 'Hardware', definition: 'The physical components of a computer system.', example: 'He is upgrading his computer hardware for better performance.', pronunciation: 'ˈhɑːrdweər', translation: 'Perangkat keras' }
    ])
  },
  grammar: {
    text: JSON.stringify({
      original: "He go to school yesterday.",
      corrected: "He went to school yesterday.",
      explanation: "Gunakan bentuk lampau (past tense) 'went' karena kalimat ini merujuk pada kejadian di masa lalu ('yesterday'). 'Go' adalah bentuk sekarang (present tense)."
    })
  },
  story: {
    text: JSON.stringify({
      story: "A curious squirrel named Squeaky lived in a large oak tree. One day, he found a shiny, round object on the ground. It was a lost monocle from a traveler. Squeaky put it over his eye and suddenly, the world looked huge and clear! He could see the tiny veins on a leaf and the small legs of an ant.\nHe used his new 'super eye' to find the best acorns in the forest. All the other squirrels were amazed. They asked him for his secret. Squeaky, being a kind squirrel, shared his monocle. But it was too big for them. Squeaky realized his advantage wasn't just the monocle, but his curiosity to try new things. From that day on, he taught his friends to be more observant and curious about the world around them.",
      vocabulary: [
        { word: 'Curious', translation: 'Penasaran' },
        { word: 'Monocle', translation: 'Kacamata berlensa tunggal' },
        { word: 'Veins', translation: 'Urat (daun)' },
        { word: 'Acorns', translation: 'Biji pohon ek' },
        { word: 'Amazed', translation: 'Kagum / Takjub' },
        { word: 'Observant', translation: 'Penuh perhatian' }
      ]
    })
  }
};

async function* mockChatStream(message) {
  const responses = {
    'hello': "Hi there! I'm SobatLingo. What's on your mind today?",
    'how are you': "I'm doing great, thank you for asking! Ready to practice some English?",
    'default': "That's an interesting point. Could you tell me more about it?"
  };
  const key = message.toLowerCase().trim();
  const responseText = responses[key] || responses['default'];
  
  // Simulate streaming by splitting the response into words
  for (const word of responseText.split(' ')) {
    await new Promise(resolve => setTimeout(resolve, 60));
    yield { text: `${word} ` };
  }
}

// A short, silent, base64 encoded WAV file. This prevents errors in the audio decoding logic.
const SILENT_AUDIO_B64 = 'UklGRiQAAABXQVZFZm10IBAAAAABAAEARKwAAIhYAQACABAAZGF0YQAAAAA=';

const ai = {
  models: {
    generateContent: (config) => new Promise(resolve => {
      setTimeout(() => {
        const prompt = config.contents;
        if (prompt.includes('vocabulary')) resolve(mockData.vocabulary);
        else if (prompt.includes('grammar')) resolve(mockData.grammar);
        else if (prompt.includes('story')) resolve(mockData.story);
        else resolve({ text: '{}' });
      }, MOCK_DELAY);
    }),
  },
  chats: {
    create: () => ({
      sendMessageStream: ({ message }) => mockChatStream(message),
    }),
  },
  live: {
    connect: (config) => new Promise(resolve => {
      const mockSession = {
        sendRealtimeInput: () => { /* Mock does nothing with input */ },
        close: () => {
          console.log("Mock session closed.");
          config.callbacks.onclose({}); // Pass an empty event object
        }
      };
      
      setTimeout(() => {
        config.callbacks.onopen();

        // Simulate a short, pre-scripted conversation
        setTimeout(() => {
          config.callbacks.onmessage({ serverContent: { inputTranscription: { text: "Hello, can you hear me?" } } });
          config.callbacks.onmessage({ serverContent: { turnComplete: true } });
        }, 2500);

        setTimeout(() => {
          config.callbacks.onmessage({ serverContent: { modelTurn: { parts: [{ inlineData: { data: SILENT_AUDIO_B64 } }] } } });
          config.callbacks.onmessage({ serverContent: { outputTranscription: { text: "Yes, I can hear you loud and clear! How are you today?" } } });
          config.callbacks.onmessage({ serverContent: { turnComplete: true } });
        }, 5000);
      }, MOCK_DELAY);

      resolve(mockSession);
    }),
  },
};


// --- COMMON ELEMENTS & UTILITIES ---
const createSpinner = () => {
    const spinner = document.createElement('div');
    spinner.className = "flex justify-center items-center";
    spinner.innerHTML = `<div class="animate-spin rounded-full h-8 w-8 border-b-2 border-indigo-600"></div>`;
    return spinner;
};

const displayError = (element, message) => {
    element.textContent = message || 'Terjadi kesalahan.';
    element.classList.remove('hidden');
};

const hideError = (element) => {
    element.classList.add('hidden');
};


// --- HISTORY SERVICE ---
const HISTORY_KEY = 'sobatlingo_conversation_history';

const getHistory = () => {
    try {
        const historyJson = localStorage.getItem(HISTORY_KEY);
        if (!historyJson) return [];
        const history = JSON.parse(historyJson);
        return history.sort((a, b) => b.id - a.id);
    } catch (error) {
        console.error("Failed to parse conversation history:", error);
        return [];
    }
};

const saveSession = (session) => {
    try {
        if (session.messages.length < 2) return;
        const history = getHistory();
        const newHistory = [session, ...history.filter(s => s.id !== session.id)];
        localStorage.setItem(HISTORY_KEY, JSON.stringify(newHistory));
    } catch (error) {
        console.error("Failed to save conversation session:", error);
    }
};

const clearHistory = () => {
    try {
        localStorage.removeItem(HISTORY_KEY);
    } catch (error) {
        console.error("Failed to clear conversation history:", error);
    }
};


// --- INITIALIZATION ON DOM LOAD ---
document.addEventListener('DOMContentLoaded', () => {
    initTabs();
    initVocabularyBuilder();
    initGrammarChecker();
    initStoryGenerator();
    initConversationPractice();
    initVoiceChat();
});

// --- TAB MANAGEMENT ---
function initTabs() {
    const tabsContainer = document.getElementById('tabs');
    const tabButtons = tabsContainer.querySelectorAll('.tab-button');
    const tabContents = document.querySelectorAll('.tab-content');

    tabsContainer.addEventListener('click', (event) => {
        // Fix for: Property 'closest' does not exist on type 'EventTarget'.
        const target = (event.target as HTMLElement).closest('button');
        if (!target) return;
        
        const tabId = target.dataset.tab;

        tabButtons.forEach(button => {
            button.classList.remove('bg-indigo-600', 'text-white', 'shadow-md');
            button.classList.add('text-slate-600', 'hover:bg-indigo-100');
        });
        target.classList.add('bg-indigo-600', 'text-white', 'shadow-md');
        target.classList.remove('text-slate-600', 'hover:bg-indigo-100');

        tabContents.forEach(content => {
            content.classList.add('hidden');
        });
        document.getElementById(tabId).classList.remove('hidden');

        if (tabId === 'riwayat') {
            initHistoryViewer();
        }
    });
}


// --- VOCABULARY BUILDER ---
function initVocabularyBuilder() {
    // Fix: Cast elements to their specific types to avoid property errors.
    const topicInput = document.getElementById('vocab-topic') as HTMLInputElement;
    const generateBtn = document.getElementById('vocab-generate-btn') as HTMLButtonElement;
    const resultsContainer = document.getElementById('vocab-results');
    const spinner = document.getElementById('vocab-spinner');
    const errorContainer = document.getElementById('vocab-error');
    const countBadge = document.getElementById('vocab-count');

    const speakWord = (word) => {
        if ('speechSynthesis' in window) {
            window.speechSynthesis.cancel();
            const utterance = new SpeechSynthesisUtterance(word);
            utterance.lang = 'en-US';
            window.speechSynthesis.speak(utterance);
        } else {
            alert("Maaf, browser Anda tidak mendukung fitur text-to-speech.");
        }
    };

    generateBtn.addEventListener('click', async () => {
        const topic = topicInput.value.trim();
        if (!topic) {
            displayError(errorContainer, 'Silakan masukkan topik.');
            return;
        }

        hideError(errorContainer);
        resultsContainer.innerHTML = '';
        spinner.innerHTML = '';
        spinner.appendChild(createSpinner());
        spinner.classList.remove('hidden');
        generateBtn.disabled = true;
        generateBtn.textContent = 'Membuat...';

        try {
            const response = await ai.models.generateContent({
                contents: `Generate vocabulary for "${topic}"`, // Prompt for mock
            });
            
            // Fix: Cast response to access 'text' property.
            const vocabulary = JSON.parse((response as { text: string }).text.trim());
            
            countBadge.textContent = `${vocabulary.length} kata ditemukan`;
            countBadge.classList.remove('hidden');

            vocabulary.forEach(item => {
                const card = document.createElement('div');
                card.className = "bg-white rounded-xl shadow-md p-6 transition-all duration-300 hover:shadow-lg flex flex-col";
                card.innerHTML = `
                    <div class="flex items-center justify-between gap-2">
                        <div class="flex items-center gap-2 flex-wrap">
                            <h3 class="text-xl font-bold text-indigo-700">${item.word}</h3>
                            <button data-word="${item.word}" class="speak-btn text-slate-500 hover:text-indigo-600 transition-colors p-1 rounded-full focus:outline-none focus:ring-2 focus:ring-indigo-300" aria-label="Dengarkan pengucapan dari ${item.word}">
                                <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-5 h-5"><path stroke-linecap="round" stroke-linejoin="round" d="M19.114 5.636a9 9 0 0 1 0 12.728M16.463 8.288a5.25 5.25 0 0 1 0 7.424M6.75 8.25l4.72-4.72a.75.75 0 0 1 1.28.53v15.88a.75.75 0 0 1-1.28.53l-4.72-4.72H4.51c-.88 0-1.704-.507-1.938-1.354A9.01 9.01 0 0 1 2.25 12c0-.83.112-1.633.322-2.396C2.806 8.756 3.63 8.25 4.51 8.25H6.75Z" /></svg>
                            </button>
                        </div>
                        <span class="text-sm text-slate-400 font-mono">/${item.pronunciation}/</span>
                    </div>
                    <div class="mt-4 space-y-2 flex-grow">
                        <p class="text-slate-600 text-sm">${item.definition}</p>
                        <p class="text-indigo-600 text-sm font-semibold">(${item.translation})</p>
                        <p class="text-slate-500 mt-4 text-sm italic">"${item.example}"</p>
                    </div>`;
                resultsContainer.appendChild(card);
            });
            // Fix for: Property 'dataset' does not exist on type 'Element'.
            resultsContainer.querySelectorAll<HTMLElement>('.speak-btn').forEach(btn => {
                btn.addEventListener('click', () => speakWord(btn.dataset.word));
            });

        } catch (err) {
            displayError(errorContainer, err.message || 'Gagal membuat kosakata.');
        } finally {
            spinner.classList.add('hidden');
            generateBtn.disabled = false;
            generateBtn.textContent = 'Buat';
        }
    });
}


// --- GRAMMAR CHECKER ---
function initGrammarChecker() {
    // Fix: Cast elements to their specific types to avoid property errors.
    const textInput = document.getElementById('grammar-text') as HTMLTextAreaElement;
    const checkBtn = document.getElementById('grammar-check-btn') as HTMLButtonElement;
    const resultContainer = document.getElementById('grammar-result');
    const spinner = document.getElementById('grammar-spinner');
    const errorContainer = document.getElementById('grammar-error');
    const countBadge = document.getElementById('grammar-count');
    let correctionCount = 0;

    checkBtn.addEventListener('click', async () => {
        const text = textInput.value.trim();
        if (!text) {
            displayError(errorContainer, 'Silakan masukkan teks untuk diperiksa.');
            return;
        }

        hideError(errorContainer);
        resultContainer.classList.add('hidden');
        spinner.innerHTML = '';
        spinner.appendChild(createSpinner());
        spinner.classList.remove('hidden');
        checkBtn.disabled = true;
        checkBtn.textContent = 'Memeriksa...';

        try {
            const response = await ai.models.generateContent({
                contents: `Check grammar for: "${text}"`, // Prompt for mock
            });
            // Fix: Cast response to access 'text' property.
            const result = JSON.parse((response as { text: string }).text.trim());

            if (result.corrected.toLowerCase() !== result.original.toLowerCase()) {
              correctionCount++;
              countBadge.textContent = `Total Koreksi: ${correctionCount}`;
              countBadge.classList.remove('hidden');
            }

            resultContainer.innerHTML = `
                <div class="bg-white rounded-xl shadow-md p-6 transition-all duration-300 hover:shadow-lg space-y-4">
                    <div>
                        <h3 class="font-semibold text-slate-500">Teks Asli</h3>
                        <p class="mt-1 p-3 bg-red-50 rounded-md text-red-800 line-through">${result.original}</p>
                    </div>
                    <div>
                        <h3 class="font-semibold text-slate-500">Teks yang Diperbaiki</h3>
                        <p class="mt-1 p-3 bg-green-50 rounded-md text-green-800">${result.corrected}</p>
                    </div>
                    <div>
                        <h3 class="font-semibold text-slate-500">Penjelasan</h3>
                        <p class="mt-1 p-3 bg-yellow-50 rounded-md text-yellow-800">${result.explanation}</p>
                    </div>
                </div>`;
            resultContainer.classList.remove('hidden');

        } catch (err) {
            displayError(errorContainer, err.message || 'Gagal memeriksa tata bahasa.');
        } finally {
            spinner.classList.add('hidden');
            checkBtn.disabled = false;
            checkBtn.textContent = 'Periksa Tata Bahasa';
        }
    });
}


// --- STORY GENERATOR ---
function initStoryGenerator() {
    // Fix: Cast elements to their specific types to avoid property errors.
    const promptInput = document.getElementById('story-prompt') as HTMLInputElement;
    const levelSelect = document.getElementById('story-level') as HTMLSelectElement;
    const generateBtn = document.getElementById('story-generate-btn') as HTMLButtonElement;
    const resultContainer = document.getElementById('story-result');
    const spinner = document.getElementById('story-spinner');
    const errorContainer = document.getElementById('story-error');
    const countBadge = document.getElementById('story-count');
    let storyCount = 0;

    generateBtn.addEventListener('click', async () => {
        const prompt = promptInput.value.trim();
        if (!prompt) {
            displayError(errorContainer, 'Silakan masukkan ide untuk cerita.');
            return;
        }

        const level = levelSelect.value;
        hideError(errorContainer);
        resultContainer.classList.add('hidden');
        spinner.innerHTML = '';
        spinner.appendChild(createSpinner());
        spinner.classList.remove('hidden');
        generateBtn.disabled = true;
        generateBtn.textContent = 'Menulis...';

        try {
            const response = await ai.models.generateContent({
                contents: `Generate story for prompt "${prompt}" at level ${level}`, // Prompt for mock
              });
            // Fix: Cast response to access 'text' property.
            const result = JSON.parse((response as { text: string }).text.trim());

            storyCount++;
            countBadge.textContent = `Cerita Dibuat: ${storyCount}`;
            countBadge.classList.remove('hidden');

            const storyParagraphs = result.story.split('\n').map(p => `<p>${p}</p>`).join('');
            const vocabList = result.vocabulary.map(v => `
                <li class="text-slate-600">
                    <span class="font-semibold text-indigo-700">${v.word}</span>: ${v.translation}
                </li>`).join('');

            resultContainer.innerHTML = `
                <div class="bg-white rounded-xl shadow-md p-6 transition-all duration-300 hover:shadow-lg space-y-6">
                    <div>
                        <h3 class="text-xl font-bold text-slate-700 mb-4 border-b pb-2">Cerita</h3>
                        <div class="prose max-w-none text-slate-700">${storyParagraphs}</div>
                    </div>
                    ${result.vocabulary && result.vocabulary.length > 0 ? `
                    <div>
                        <h3 class="text-xl font-bold text-slate-700 mb-4 border-b pb-2">Kosakata Penting</h3>
                        <ul class="space-y-2 list-disc list-inside">${vocabList}</ul>
                    </div>` : ''}
                </div>`;
            resultContainer.classList.remove('hidden');

        } catch (err) {
            displayError(errorContainer, err.message || 'Gagal membuat cerita.');
        } finally {
            spinner.classList.add('hidden');
            generateBtn.disabled = false;
            generateBtn.textContent = 'Buat Cerita';
        }
    });
}


// --- CONVERSATION PRACTICE ---
function initConversationPractice() {
    const container = document.getElementById('chat-container');
    // Fix: Cast elements to their specific types to avoid property errors.
    const input = document.getElementById('chat-input') as HTMLInputElement;
    const sendBtn = document.getElementById('chat-send-btn') as HTMLButtonElement;
    const errorContainer = document.getElementById('chat-error');
    const countBadge = document.getElementById('chat-count');
    
    let messages = [{ role: 'model', content: "Halo! Saya SobatLingo, tutor Bahasa Inggris AI Anda. Mari kita mengobrol. Apa yang ingin kamu bicarakan hari ini?" }];
    let isLoading = false;
    let chat;

    const renderMessages = () => {
        container.innerHTML = '';
        messages.forEach(msg => {
            const wrapper = document.createElement('div');
            wrapper.className = `flex ${msg.role === 'user' ? 'justify-end' : 'justify-start'}`;
            wrapper.innerHTML = `<div class="max-w-xs md:max-w-md lg:max-w-lg px-4 py-2 rounded-2xl ${msg.role === 'user' ? 'bg-indigo-600 text-white' : 'bg-slate-200 text-slate-800'}">${msg.content || '<span class="inline-block w-2 h-4 bg-slate-500 animate-pulse rounded-full"></span>'}</div>`;
            container.appendChild(wrapper);
        });
        container.scrollTop = container.scrollHeight;
    };

    const handleSendMessage = async () => {
        const userInput = input.value.trim();
        if (!userInput || isLoading) return;

        isLoading = true;
        sendBtn.disabled = true;
        input.disabled = true;
        hideError(errorContainer);

        messages.push({ role: 'user', content: userInput });
        const userMessagesCount = messages.filter(msg => msg.role === 'user').length;
        countBadge.textContent = `${userMessagesCount} balasan Anda`;
        countBadge.classList.remove('hidden');
        renderMessages();

        input.value = '';
        
        // Add placeholder for model response
        messages.push({ role: 'model', content: '' });
        renderMessages();

        try {
            if(!chat) {
                // Fix: The mock `create` function expects 0 arguments.
                chat = ai.chats.create();
            }

            const stream = await chat.sendMessageStream({ message: userInput });

            let modelResponse = '';
            for await (const chunk of stream) {
                modelResponse += chunk.text;
                messages[messages.length - 1].content = modelResponse;
                renderMessages();
            }

        } catch (err) {
            displayError(errorContainer, err.message || 'Maaf, terjadi kesalahan.');
            messages.pop(); // remove user message on error
            messages.pop(); // remove model placeholder
            renderMessages();
        } finally {
            isLoading = false;
            sendBtn.disabled = false;
            input.disabled = false;
            input.focus();
            if (messages.length > 1) {
              saveSession({ id: Date.now(), type: 'Teks', date: new Date().toISOString(), messages });
            }
        }
    };
    
    renderMessages();
    sendBtn.addEventListener('click', handleSendMessage);
    input.addEventListener('keypress', (e) => e.key === 'Enter' && handleSendMessage());
}


// --- VOICE CHAT ---
// Audio helper functions
function decode(base64) {
    const binaryString = atob(base64);
    const len = binaryString.length;
    const bytes = new Uint8Array(len);
    for (let i = 0; i < len; i++) {
        bytes[i] = binaryString.charCodeAt(i);
    }
    return bytes;
}
async function decodeAudioData(data, ctx, sampleRate, numChannels) {
    const dataInt16 = new Int16Array(data.buffer);
    const frameCount = dataInt16.length / numChannels;
    const buffer = ctx.createBuffer(numChannels, frameCount, sampleRate);
    for (let channel = 0; channel < numChannels; channel++) {
        const channelData = buffer.getChannelData(channel);
        for (let i = 0; i < frameCount; i++) {
            channelData[i] = dataInt16[i * numChannels + channel] / 32768.0;
        }
    }
    return buffer;
}
function encode(bytes) {
    let binary = '';
    const len = bytes.byteLength;
    for (let i = 0; i < len; i++) {
        binary += String.fromCharCode(bytes[i]);
    }
    return btoa(binary);
}

function initVoiceChat() {
    const toggleBtn = document.getElementById('voice-toggle-btn');
    const startContent = document.getElementById('voice-btn-content-start');
    const stopContent = document.getElementById('voice-btn-content-stop');
    const statusEl = document.getElementById('voice-status');
    const errorEl = document.getElementById('voice-error');
    const transcriptEl = document.getElementById('voice-transcript');

    let isSessionActive = false;
    let sessionPromise = null;
    let mediaStream = null;
    let inputAudioContext = null;
    let outputAudioContext = null;
    let scriptProcessor = null;
    let nextStartTime = 0;
    // Fix for: Property 'stop' does not exist on type 'unknown'.
    let sources = new Set<AudioBufferSourceNode>();
    let transcript = [];
    let currentInputTranscription = '';
    let currentOutputTranscription = '';

    const renderTranscript = () => {
        if(transcript.length === 0) {
            transcriptEl.innerHTML = `<p class="text-slate-400 h-full flex items-center justify-center">Transkrip percakapan akan muncul di sini.</p>`;
            return;
        }
        transcriptEl.innerHTML = '';
        transcript.forEach(msg => {
            const wrapper = document.createElement('div');
            wrapper.className = `flex ${msg.role === 'user' ? 'justify-end' : 'justify-start'}`;
            wrapper.innerHTML = `
                <div class="max-w-xs md:max-w-md lg:max-w-lg px-4 py-2 rounded-2xl text-left ${msg.role === 'user' ? 'bg-indigo-600 text-white' : 'bg-slate-200 text-slate-800'}">
                    <span class="font-bold text-sm block">${msg.role === 'user' ? 'Anda' : 'SobatLingo'}</span>
                    ${msg.content}
                </div>`;
            transcriptEl.appendChild(wrapper);
        });
        transcriptEl.scrollTop = transcriptEl.scrollHeight;
    };

    const stopSession = async () => {
        if (transcript.length > 0) {
            saveSession({ id: Date.now(), type: 'Suara', date: new Date().toISOString(), messages: transcript });
        }
        isSessionActive = false;
        statusEl.textContent = 'Sesi berakhir.';
        toggleBtn.classList.remove('bg-red-600', 'hover:bg-red-700', 'focus:ring-red-300');
        toggleBtn.classList.add('bg-indigo-600', 'hover:bg-indigo-700', 'focus:ring-indigo-300');
        startContent.classList.remove('hidden');
        stopContent.classList.add('hidden');
        
        if (sessionPromise) {
            try {
                const session = await sessionPromise;
                session.close();
            } catch (e) { console.error("Error closing session:", e); }
            sessionPromise = null;
        }
        if (mediaStream) {
            mediaStream.getTracks().forEach(track => track.stop());
            mediaStream = null;
        }
        if (scriptProcessor) {
            scriptProcessor.disconnect();
            scriptProcessor = null;
        }
        if (inputAudioContext && inputAudioContext.state !== 'closed') {
            await inputAudioContext.close();
        }
        if (outputAudioContext && outputAudioContext.state !== 'closed') {
            sources.forEach(source => source.stop());
            sources.clear();
            await outputAudioContext.close();
        }
    };

    const startSession = async () => {
        hideError(errorEl);
        transcript = [];
        renderTranscript();
        statusEl.textContent = 'Meminta izin mikrofon...';
        
        try {
            mediaStream = await navigator.mediaDevices.getUserMedia({ audio: true });
        } catch (e) {
            displayError(errorEl, 'Izin mikrofon ditolak. Izinkan akses mikrofon untuk menggunakan fitur ini.');
            statusEl.textContent = 'Izin mikrofon diperlukan.';
            return;
        }

        isSessionActive = true;
        statusEl.textContent = 'Menghubungkan ke SobatLingo...';
        toggleBtn.classList.add('bg-red-600', 'hover:bg-red-700', 'focus:ring-red-300');
        toggleBtn.classList.remove('bg-indigo-600', 'hover:bg-indigo-700', 'focus:ring-indigo-300');
        startContent.classList.add('hidden');
        stopContent.classList.remove('hidden');

        // Fix for: Property 'webkitAudioContext' does not exist on type 'Window & typeof globalThis'.
        const AudioContext = window.AudioContext || (window as any).webkitAudioContext;
        inputAudioContext = new AudioContext({ sampleRate: 16000 });
        outputAudioContext = new AudioContext({ sampleRate: 24000 });
        nextStartTime = 0;
        
        sessionPromise = ai.live.connect({
            callbacks: {
                onopen: () => {
                    statusEl.textContent = 'Terhubung! Anda bisa mulai berbicara.';
                    const source = inputAudioContext.createMediaStreamSource(mediaStream);
                    scriptProcessor = inputAudioContext.createScriptProcessor(4096, 1, 1);

                    scriptProcessor.onaudioprocess = (audioProcessingEvent) => {
                        const inputData = audioProcessingEvent.inputBuffer.getChannelData(0);
                        const l = inputData.length;
                        const int16 = new Int16Array(l);
                        for (let i = 0; i < l; i++) { int16[i] = inputData[i] * 32768; }
                        const pcmBlob = { data: encode(new Uint8Array(int16.buffer)), mimeType: 'audio/pcm;rate=16000' };
                        sessionPromise?.then((session) => session.sendRealtimeInput({ media: pcmBlob }));
                    };
                    source.connect(scriptProcessor);
                    scriptProcessor.connect(inputAudioContext.destination);
                },
                onmessage: async (message) => {
                    if (message.serverContent?.inputTranscription) {
                        currentInputTranscription += message.serverContent.inputTranscription.text;
                    }
                    if (message.serverContent?.outputTranscription) {
                        currentOutputTranscription += message.serverContent.outputTranscription.text;
                    }
                    if (message.serverContent?.turnComplete) {
                        const fullInput = currentInputTranscription.trim();
                        const fullOutput = currentOutputTranscription.trim();
                        if (fullInput) transcript.push({ role: 'user', content: fullInput });
                        if (fullOutput) transcript.push({ role: 'model', content: fullOutput });
                        renderTranscript();
                        currentInputTranscription = '';
                        currentOutputTranscription = '';
                    }
                    const base64Audio = message.serverContent?.modelTurn?.parts[0]?.inlineData.data;
                    if (base64Audio && outputAudioContext) {
                        statusEl.textContent = 'SobatLingo sedang berbicara...';
                        const audioBytes = decode(base64Audio);
                        const audioBuffer = await decodeAudioData(audioBytes, outputAudioContext, 24000, 1);
                        nextStartTime = Math.max(nextStartTime, outputAudioContext.currentTime);
                        const source = outputAudioContext.createBufferSource();
                        source.buffer = audioBuffer;
                        source.connect(outputAudioContext.destination);
                        source.addEventListener('ended', () => {
                            sources.delete(source);
                            if (sources.size === 0) statusEl.textContent = 'Sekarang giliran Anda berbicara...';
                        });
                        source.start(nextStartTime);
                        nextStartTime += audioBuffer.duration;
                        sources.add(source);
                    }
                },
                onerror: (e) => {
                    console.error(e);
                    displayError(errorEl, 'Koneksi terputus. Silakan coba lagi.');
                    stopSession();
                },
                onclose: () => {
                    stopSession();
                },
            },
        });
    };

    toggleBtn.addEventListener('click', () => {
        isSessionActive ? stopSession() : startSession();
    });
}


// --- HISTORY VIEWER ---
function initHistoryViewer() {
    const container = document.getElementById('history-container');
    const clearBtn = document.getElementById('history-clear-btn');
    
    const history = getHistory();

    const formatDate = (isoString) => new Date(isoString).toLocaleString('id-ID', { dateStyle: 'medium', timeStyle: 'short' });

    if (history.length === 0) {
        container.innerHTML = `
            <div class="bg-white rounded-xl shadow-md p-6 transition-all duration-300 hover:shadow-lg text-center py-12">
                <p class="text-slate-500">Tidak ada riwayat percakapan yang tersimpan.</p>
                <p class="text-slate-400 text-sm mt-2">Mulai sesi latihan untuk menyimpannya di sini.</p>
            </div>`;
        clearBtn.classList.add('hidden');
        return;
    }

    clearBtn.classList.remove('hidden');
    clearBtn.onclick = () => {
        if (window.confirm("Apakah Anda yakin ingin menghapus seluruh riwayat percakapan?")) {
            clearHistory();
            initHistoryViewer(); // Re-render
        }
    };
    
    let listHTML = '';
    history.forEach(session => {
        listHTML += `
            <div data-session-id="${session.id}" class="session-item p-4 rounded-lg cursor-pointer border-2 transition-all bg-white border-slate-200 hover:border-slate-300 hover:shadow-sm">
                <div class="flex items-center gap-3 flex-wrap">
                    <span class="px-2 py-1 text-xs font-bold rounded-full ${session.type === 'Teks' ? 'bg-blue-100 text-blue-800' : 'bg-purple-100 text-purple-800'}">${session.type}</span>
                    <span class="font-semibold text-slate-700 text-sm">${formatDate(session.date)}</span>
                </div>
                <p class="text-sm text-slate-500 mt-2 truncate">${session.messages[session.messages.length - 1]?.content}</p>
            </div>`;
    });

    container.innerHTML = `
        <div class="grid grid-cols-1 md:grid-cols-3 gap-6">
            <div class="md:col-span-1 space-y-3 max-h-[60vh] overflow-y-auto pr-2 -mr-2">${listHTML}</div>
            <div id="history-transcript-viewer" class="md:col-span-2">
                <div class="bg-white rounded-xl shadow-md p-6 h-full flex items-center justify-center text-center">
                    <div>
                        <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-12 h-12 mx-auto text-slate-300"><path stroke-linecap="round" stroke-linejoin="round" d="M3.75 12h16.5m-16.5 3.75h16.5M3.75 19.5h16.5M5.625 4.5h12.75a1.875 1.875 0 0 1 0 3.75H5.625a1.875 1.875 0 0 1 0-3.75Z" /></svg>
                        <p class="text-slate-500 mt-4 font-semibold">Pilih sebuah sesi dari daftar</p>
                        <p class="text-slate-400 text-sm">Transkrip lengkap akan ditampilkan di sini.</p>
                    </div>
                </div>
            </div>
        </div>`;
        
    let selectedSessionId = null;
    // Fix for: Property 'dataset' does not exist on type 'Element'.
    container.querySelectorAll<HTMLElement>('.session-item').forEach(item => {
        item.addEventListener('click', () => {
            const sessionId = item.dataset.sessionId;
            if (selectedSessionId === sessionId) return;

            // Update item styles
            container.querySelectorAll('.session-item').forEach(el => {
                el.classList.remove('bg-indigo-50', 'border-indigo-400', 'shadow-md');
                el.classList.add('bg-white', 'border-slate-200');
            });
            item.classList.add('bg-indigo-50', 'border-indigo-400', 'shadow-md');
            selectedSessionId = sessionId;

            // Render transcript
            const session = history.find(s => s.id == sessionId);
            let transcriptHTML = '';
            session.messages.forEach(msg => {
                transcriptHTML += `
                    <div class="flex ${msg.role === 'user' ? 'justify-end' : 'justify-start'}">
                        <div class="max-w-xs md:max-w-md lg:max-w-lg px-4 py-2 rounded-2xl ${msg.role === 'user' ? 'bg-indigo-600 text-white' : 'bg-slate-200 text-slate-800'}">
                            <span class="font-bold text-sm block capitalize">${msg.role === 'user' ? 'Anda' : 'SobatLingo'}</span>
                            ${msg.content}
                        </div>
                    </div>`;
            });

            document.getElementById('history-transcript-viewer').innerHTML = `
                <div class="bg-white rounded-xl shadow-md p-6 h-full flex flex-col">
                    <h3 class="text-lg font-bold text-slate-700 border-b pb-2 mb-4 flex-shrink-0">
                        Transkrip Sesi (${session.type} - ${formatDate(session.date)})
                    </h3>
                    <div class="space-y-4 flex-grow h-[55vh] overflow-y-auto pr-2 -mr-2">${transcriptHTML}</div>
                </div>`;
        });
    });
}