package com.example.notesai

import android.content.Context
import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.PreferenceDataStoreFactory
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.preferencesDataStoreFile
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.example.notesai.data.local.DataStoreManager
import com.example.notesai.data.model.*
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.cancel
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.runBlocking
import org.junit.After
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.Assert.*
import java.util.UUID

/**
 * Unit Test untuk AI Chat Functionality
 * Coverage:
 * - TC-05: AI chat menjawab dengan konteks note (gunakan 1-2 contoh note)
 */
@RunWith(AndroidJUnit4::class)
class AIChatFunctionalityTest {

    private lateinit var context: Context
    private lateinit var dataStore: DataStore<Preferences>
    private lateinit var dataStoreManager: DataStoreManager
    private lateinit var testScope: CoroutineScope

    @Before
    fun setup() = runBlocking {
        context = ApplicationProvider.getApplicationContext()
        testScope = CoroutineScope(SupervisorJob())

        val testDataStoreName = "test_ai_chat_prefs_${UUID.randomUUID()}"
        dataStore = PreferenceDataStoreFactory.create(
            scope = testScope,
            produceFile = { context.preferencesDataStoreFile(testDataStoreName) }
        )

        dataStoreManager = DataStoreManager(context)

        // Clear all data before each test
        dataStoreManager.saveNotes(emptyList())
        dataStoreManager.saveCategories(emptyList())
        dataStoreManager.saveChatHistory(emptyList())
    }

    @After
    fun tearDown() {
        testScope.cancel()
    }

    // ================== TC-05: AI CHAT WITH CONTEXT ==================

    @Test
    fun testBuildNotesContext_shouldIncludeAllNotes() = runBlocking {
        // Given - Create sample notes
        val notes = listOf(
            Note(
                id = "note_001",
                categoryId = "cat_work",
                title = "Project Meeting",
                content = "Discussed Q4 goals and timeline for new features",
                isArchived = false,
                isDeleted = false
            ),
            Note(
                id = "note_002",
                categoryId = "cat_work",
                title = "Technical Specs",
                content = "API design for user authentication module",
                isArchived = false,
                isDeleted = false
            )
        )

        dataStoreManager.saveNotes(notes)

        // When - Build context (simulating AIHelperScreen context building)
        val savedNotes = dataStoreManager.notesFlow.first()
        val filteredNotes = savedNotes.filter { !it.isArchived && !it.isDeleted }

        val notesContext = buildString {
            appendLine("Data catatan pengguna:")
            appendLine("Total catatan: ${filteredNotes.size}")
            appendLine()
            appendLine("Daftar catatan:")
            filteredNotes.forEach { note ->
                appendLine("- Judul: ${note.title}")
                appendLine("  Isi: ${note.content.take(100)}")
                appendLine()
            }
        }

        // Then - Context should contain both notes
        assertTrue(notesContext.contains("Total catatan: 2"))
        assertTrue(notesContext.contains("Project Meeting"))
        assertTrue(notesContext.contains("Technical Specs"))
        assertTrue(notesContext.contains("Discussed Q4 goals"))
        assertTrue(notesContext.contains("API design"))
    }

    @Test
    fun testBuildNotesContext_shouldFilterByCategory() = runBlocking {
        // Given - Notes in different categories
        val notes = listOf(
            Note(id = "note_001", categoryId = "cat_work", title = "Work Note", content = "Work content", isArchived = false),
            Note(id = "note_002", categoryId = "cat_personal", title = "Personal Note", content = "Personal content", isArchived = false),
            Note(id = "note_003", categoryId = "cat_work", title = "Another Work", content = "More work", isArchived = false)
        )

        dataStoreManager.saveNotes(notes)

        // When - Build context for specific category
        val savedNotes = dataStoreManager.notesFlow.first()
        val selectedCategoryId = "cat_work"
        val filteredNotes = savedNotes.filter {
            it.categoryId == selectedCategoryId && !it.isArchived && !it.isDeleted
        }

        val notesContext = buildString {
            appendLine("Data catatan pengguna:")
            appendLine("Total catatan: ${filteredNotes.size}")
            appendLine("Kategori: Work")
            appendLine()
            filteredNotes.forEach { note ->
                appendLine("- Judul: ${note.title}")
                appendLine("  Isi: ${note.content}")
            }
        }

        // Then - Should only include work category notes
        assertTrue(notesContext.contains("Total catatan: 2"))
        assertTrue(notesContext.contains("Work Note"))
        assertTrue(notesContext.contains("Another Work"))
        assertFalse(notesContext.contains("Personal Note"))
    }

    @Test
    fun testBuildNotesContext_shouldExcludeArchivedNotes() = runBlocking {
        // Given - Mix of archived and active notes
        val notes = listOf(
            Note(id = "note_001", categoryId = "cat_001", title = "Active Note", content = "Active", isArchived = false),
            Note(id = "note_002", categoryId = "cat_001", title = "Archived Note", content = "Archived", isArchived = true),
            Note(id = "note_003", categoryId = "cat_001", title = "Another Active", content = "Active", isArchived = false)
        )

        dataStoreManager.saveNotes(notes)

        // When - Build context excluding archived
        val savedNotes = dataStoreManager.notesFlow.first()
        val filteredNotes = savedNotes.filter { !it.isArchived && !it.isDeleted }

        val notesContext = buildString {
            appendLine("Total catatan: ${filteredNotes.size}")
            filteredNotes.forEach { note ->
                appendLine("- ${note.title}")
            }
        }

        // Then - Should exclude archived notes
        assertTrue(notesContext.contains("Total catatan: 2"))
        assertTrue(notesContext.contains("Active Note"))
        assertTrue(notesContext.contains("Another Active"))
        assertFalse(notesContext.contains("Archived Note"))
    }

    @Test
    fun testSaveChatHistory_shouldPersist() = runBlocking {
        // Given - Clear existing data first
        dataStoreManager.saveChatHistory(emptyList())

        // Chat messages
        val messages = listOf(
            ChatMessage(id = "msg_001", message = "Apa isi catatan saya?", isUser = true),
            ChatMessage(id = "msg_002", message = "Anda memiliki 3 catatan tentang project meeting", isUser = false)
        )

        val chatHistory = ChatHistory(
            id = "chat_001",
            categoryId = "cat_work",
            categoryName = "Work",
            messages = messages.map { it.toSerializable() },
            lastMessagePreview = "Anda memiliki 3 catatan...",
            timestamp = System.currentTimeMillis(),
            isDeleted = false
        )

        // When - Save chat history
        dataStoreManager.addChatHistory(chatHistory)

        // Then - Should be saved and retrievable
        val savedHistories = dataStoreManager.chatHistoryFlow.first()
        assertEquals(1, savedHistories.size)
        assertEquals("chat_001", savedHistories[0].id)
        assertEquals("Work", savedHistories[0].categoryName)
        assertEquals(2, savedHistories[0].messages.size)
    }

    @Test
    fun testLoadChatHistory_shouldRestoreMessages() = runBlocking {
        // Given - Saved chat history
        val messages = listOf(
            ChatMessage(id = "msg_001", message = "Berapa banyak catatan saya?", isUser = true),
            ChatMessage(id = "msg_002", message = "Anda memiliki 5 catatan", isUser = false),
            ChatMessage(id = "msg_003", message = "Apa topik utamanya?", isUser = true),
            ChatMessage(id = "msg_004", message = "Topik utama adalah project planning", isUser = false)
        )

        val chatHistory = ChatHistory(
            id = "chat_001",
            categoryId = null,
            categoryName = "Semua Kategori",
            messages = messages.map { it.toSerializable() },
            lastMessagePreview = "Topik utama adalah project...",
            timestamp = System.currentTimeMillis()
        )

        dataStoreManager.addChatHistory(chatHistory)

        // When - Load chat history
        val savedHistories = dataStoreManager.chatHistoryFlow.first()
        val loadedHistory = savedHistories.first()
        val restoredMessages = loadedHistory.messages.map { it.toChatMessage() }

        // Then - Messages should be restored correctly
        assertEquals(4, restoredMessages.size)
        assertEquals("Berapa banyak catatan saya?", restoredMessages[0].message)
        assertTrue(restoredMessages[0].isUser)
        assertEquals("Anda memiliki 5 catatan", restoredMessages[1].message)
        assertFalse(restoredMessages[1].isUser)
    }

    @Test
    fun testChatPreview_shouldTruncateLongMessages() = runBlocking {
        // Given - Long message
        val longMessage = "Ini adalah pesan yang sangat panjang yang harus dipotong menjadi preview yang lebih pendek untuk ditampilkan di list"

        // When - Create preview (simulating toSafeChatPreview function)
        val maxLength = 30
        val preview = if (longMessage.length > maxLength) {
            longMessage.take(maxLength).trim() + "..."
        } else {
            longMessage.trim()
        }

        // Then - Should be truncated
        assertTrue(preview.length <= maxLength + 3) // +3 for "..."
        assertTrue(preview.endsWith("..."))
        assertTrue(preview.startsWith("Ini adalah pesan"))
    }

    @Test
    fun testMultipleChatHistories_shouldSortByTimestamp() = runBlocking {
        // Given - Clear existing data first
        dataStoreManager.saveChatHistory(emptyList())

        // Multiple chat histories with different timestamps
        val now = System.currentTimeMillis()

        val histories = listOf(
            ChatHistory(
                id = "chat_001",
                categoryId = null,
                categoryName = "Semua",
                messages = listOf(ChatMessage(message = "Old chat", isUser = true).toSerializable()),
                lastMessagePreview = "Old chat",
                timestamp = now - 3000,
                isDeleted = false
            ),
            ChatHistory(
                id = "chat_002",
                categoryId = null,
                categoryName = "Semua",
                messages = listOf(ChatMessage(message = "Recent chat", isUser = true).toSerializable()),
                lastMessagePreview = "Recent chat",
                timestamp = now - 1000,
                isDeleted = false
            ),
            ChatHistory(
                id = "chat_003",
                categoryId = null,
                categoryName = "Semua",
                messages = listOf(ChatMessage(message = "Newest chat", isUser = true).toSerializable()),
                lastMessagePreview = "Newest chat",
                timestamp = now,
                isDeleted = false
            )
        )

        // When - Save all histories
        histories.forEach { dataStoreManager.addChatHistory(it) }

        // Then - Should be sorted by timestamp (newest first)
        val savedHistories = dataStoreManager.chatHistoryFlow.first()
        assertEquals(3, savedHistories.size)
        assertEquals("Newest chat", savedHistories[0].lastMessagePreview)
        assertEquals("Recent chat", savedHistories[1].lastMessagePreview)
        assertEquals("Old chat", savedHistories[2].lastMessagePreview)
    }

    @Test
    fun testUpdateChatHistory_shouldUpdateExisting() = runBlocking {
        // Given - Clear existing data first
        dataStoreManager.saveChatHistory(emptyList())

        // Existing chat history
        val initialHistory = ChatHistory(
            id = "chat_update_001",
            categoryId = "cat_work",
            categoryName = "Work",
            messages = listOf(
                ChatMessage(message = "Hello", isUser = true).toSerializable()
            ),
            lastMessagePreview = "Hello",
            timestamp = System.currentTimeMillis(),
            isDeleted = false
        )

        dataStoreManager.addChatHistory(initialHistory)

        // When - Update with new messages
        val updatedHistory = initialHistory.copy(
            messages = initialHistory.messages + ChatMessage(message = "How can I help?", isUser = false).toSerializable(),
            lastMessagePreview = "How can I help?"
        )

        dataStoreManager.addChatHistory(updatedHistory)

        // Then - Should update existing history
        val savedHistories = dataStoreManager.chatHistoryFlow.first()
        assertEquals(1, savedHistories.size) // Should still be 1, not 2
        assertEquals(2, savedHistories[0].messages.size)
        assertEquals("How can I help?", savedHistories[0].lastMessagePreview)
    }

    @Test
    fun testDeleteChatHistory_shouldMarkAsDeleted() = runBlocking {
        // Given - Clear existing data first
        dataStoreManager.saveChatHistory(emptyList())

        // Chat history
        val chatHistory = ChatHistory(
            id = "chat_delete_001",
            categoryId = null,
            categoryName = "Semua",
            messages = listOf(ChatMessage(message = "Test", isUser = true).toSerializable()),
            lastMessagePreview = "Test",
            timestamp = System.currentTimeMillis(),
            isDeleted = false
        )

        dataStoreManager.addChatHistory(chatHistory)

        // Verify it's saved
        val beforeDelete = dataStoreManager.chatHistoryFlow.first()
        assertEquals(1, beforeDelete.size)

        // When - Delete chat history
        dataStoreManager.deleteChatHistory("chat_delete_001")

        // Then - Should be filtered out (soft deleted)
        val savedHistories = dataStoreManager.chatHistoryFlow.first()
        assertEquals(0, savedHistories.size)
    }

    @Test
    fun testCustomChatTitle_shouldPersist() = runBlocking {
        // Given - Clear existing data first
        dataStoreManager.saveChatHistory(emptyList())

        // Chat history with custom title
        val chatHistory = ChatHistory(
            id = "chat_custom_001",
            categoryId = null,
            categoryName = "Semua",
            messages = listOf(ChatMessage(message = "Question", isUser = true).toSerializable()),
            lastMessagePreview = "Question",
            customTitle = "My Custom Chat Title",
            timestamp = System.currentTimeMillis(),
            isDeleted = false
        )

        dataStoreManager.addChatHistory(chatHistory)

        // Then - Custom title should be saved
        val savedHistories = dataStoreManager.chatHistoryFlow.first()
        assertEquals(1, savedHistories.size)
        assertEquals("My Custom Chat Title", savedHistories[0].customTitle)
    }

    @Test
    fun testUpdateChatTitle_shouldUpdate() = runBlocking {
        // Given - Clear existing data first
        dataStoreManager.saveChatHistory(emptyList())

        // Chat history
        val chatHistory = ChatHistory(
            id = "chat_title_001",
            categoryId = null,
            categoryName = "Semua",
            messages = listOf(ChatMessage(message = "Test", isUser = true).toSerializable()),
            lastMessagePreview = "Test",
            customTitle = null,
            timestamp = System.currentTimeMillis(),
            isDeleted = false
        )

        dataStoreManager.addChatHistory(chatHistory)

        // When - Update title
        dataStoreManager.updateChatHistoryTitle("chat_title_001", "Updated Title")

        // Then - Title should be updated
        val savedHistories = dataStoreManager.chatHistoryFlow.first()
        assertEquals("Updated Title", savedHistories[0].customTitle)
    }

    @Test
    fun testChatWithContext_shouldBuildCorrectPrompt() = runBlocking {
        // Given - Sample notes for context
        val notes = listOf(
            Note(
                id = "note_001",
                categoryId = "cat_work",
                title = "Sprint Planning",
                content = "Plan features for next sprint: authentication, dashboard, notifications",
                isArchived = false,
                isDeleted = false
            ),
            Note(
                id = "note_002",
                categoryId = "cat_work",
                title = "API Design",
                content = "REST API endpoints for user management and data sync",
                isArchived = false,
                isDeleted = false
            )
        )

        dataStoreManager.saveNotes(notes)

        // When - Build full prompt with context
        val savedNotes = dataStoreManager.notesFlow.first()
        val filteredNotes = savedNotes.filter { !it.isArchived && !it.isDeleted }

        val notesContext = buildString {
            appendLine("Data catatan pengguna:")
            appendLine("Total catatan: ${filteredNotes.size}")
            appendLine()
            appendLine("Daftar catatan:")
            filteredNotes.take(10).forEach { note ->
                appendLine("- Judul: ${note.title}")
                appendLine("  Isi: ${note.content.take(100)}")
                appendLine()
            }
        }

        val userPrompt = "Apa yang perlu saya lakukan di sprint berikutnya?"
        val fullPrompt = "$notesContext\n\nPertanyaan: $userPrompt\n\nBerikan jawaban dalam bahasa Indonesia yang natural dan membantu."

        // Then - Prompt should contain context and question
        assertTrue(fullPrompt.contains("Total catatan: 2"))
        assertTrue(fullPrompt.contains("Sprint Planning"))
        assertTrue(fullPrompt.contains("authentication, dashboard"))
        assertTrue(fullPrompt.contains("Pertanyaan: Apa yang perlu saya lakukan"))
    }

    @Test
    fun testChatMessageConversion_shouldPreserveData() = runBlocking {
        // Given - Original ChatMessage
        val originalMessage = ChatMessage(
            id = "msg_001",
            message = "Test message content",
            isUser = true,
            timestamp = 1234567890L
        )

        // When - Convert to serializable and back
        val serializable = originalMessage.toSerializable()
        val converted = serializable.toChatMessage()

        // Then - All data should be preserved
        assertEquals(originalMessage.id, converted.id)
        assertEquals(originalMessage.message, converted.message)
        assertEquals(originalMessage.isUser, converted.isUser)
        assertEquals(originalMessage.timestamp, converted.timestamp)
    }

    @Test
    fun testEmptyNotesContext_shouldHandleGracefully() = runBlocking {
        // Given - No notes
        dataStoreManager.saveNotes(emptyList())

        // When - Build context
        val savedNotes = dataStoreManager.notesFlow.first()
        val filteredNotes = savedNotes.filter { !it.isArchived && !it.isDeleted }

        val notesContext = buildString {
            appendLine("Data catatan pengguna:")
            appendLine("Total catatan: ${filteredNotes.size}")
            if (filteredNotes.isEmpty()) {
                appendLine("Belum ada catatan tersimpan.")
            }
        }

        // Then - Should handle empty case
        assertTrue(notesContext.contains("Total catatan: 0"))
        assertTrue(notesContext.contains("Belum ada catatan"))
    }
}