package com.rssuper.database

import android.content.Context
import androidx.room.Room
import androidx.test.core.app.ApplicationProvider
import com.rssuper.database.daos.SearchHistoryDao
import com.rssuper.database.entities.SearchHistoryEntity
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.test.runTest
import org.junit.After
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNotNull
import org.junit.Assert.assertNull
import org.junit.Before
import org.junit.Test
import java.util.Date

class SearchHistoryDaoTest {
    
    private lateinit var database: RssDatabase
    private lateinit var dao: SearchHistoryDao
    
    @Before
    fun createDb() {
        val context = ApplicationProvider.getApplicationContext<Context>()
        database = Room.inMemoryDatabaseBuilder(
            context,
            RssDatabase::class.java
        )
            .allowMainThreadQueries()
            .build()
        dao = database.searchHistoryDao()
    }
    
    @After
    fun closeDb() {
        database.close()
    }
    
    @Test
    fun insertAndGetSearchHistory() = runTest {
        val search = createTestSearch("1", "kotlin")
        
        dao.insertSearchHistory(search)
        
        val result = dao.getSearchHistoryById("1")
        assertNotNull(result)
        assertEquals("1", result?.id)
        assertEquals("kotlin", result?.query)
    }
    
    @Test
    fun getAllSearchHistory() = runTest {
        val search1 = createTestSearch("1", "kotlin")
        val search2 = createTestSearch("2", "android")
        val search3 = createTestSearch("3", "room database")
        
        dao.insertSearchHistories(listOf(search1, search2, search3))
        
        val result = dao.getAllSearchHistory().first()
        assertEquals(3, result.size)
    }
    
    @Test
    fun searchHistory() = runTest {
        val search1 = createTestSearch("1", "kotlin coroutines")
        val search2 = createTestSearch("2", "android kotlin")
        val search3 = createTestSearch("3", "java")
        
        dao.insertSearchHistories(listOf(search1, search2, search3))
        
        val result = dao.searchHistory("kotlin").first()
        assertEquals(2, result.size)
    }
    
    @Test
    fun getRecentSearches() = runTest {
        val search1 = createTestSearch("1", "query1", timestamp = Date(System.currentTimeMillis() - 300000))
        val search2 = createTestSearch("2", "query2", timestamp = Date(System.currentTimeMillis() - 200000))
        val search3 = createTestSearch("3", "query3", timestamp = Date(System.currentTimeMillis() - 100000))
        
        dao.insertSearchHistories(listOf(search1, search2, search3))
        
        val result = dao.getRecentSearches(2).first()
        assertEquals(2, result.size)
        assertEquals("3", result[0].id)
        assertEquals("2", result[1].id)
    }
    
    @Test
    fun getSearchHistoryCount() = runTest {
        val search1 = createTestSearch("1", "query1")
        val search2 = createTestSearch("2", "query2")
        val search3 = createTestSearch("3", "query3")
        
        dao.insertSearchHistories(listOf(search1, search2, search3))
        
        val count = dao.getSearchHistoryCount().first()
        assertEquals(3, count)
    }
    
    @Test
    fun updateSearchHistory() = runTest {
        val search = createTestSearch("1", "old query")
        
        dao.insertSearchHistory(search)
        
        val updated = search.copy(query = "new query")
        dao.updateSearchHistory(updated)
        
        val result = dao.getSearchHistoryById("1")
        assertEquals("new query", result?.query)
    }
    
    @Test
    fun deleteSearchHistory() = runTest {
        val search = createTestSearch("1", "kotlin")
        
        dao.insertSearchHistory(search)
        dao.deleteSearchHistory(search)
        
        val result = dao.getSearchHistoryById("1")
        assertNull(result)
    }
    
    @Test
    fun deleteSearchHistoryById() = runTest {
        val search = createTestSearch("1", "kotlin")
        
        dao.insertSearchHistory(search)
        dao.deleteSearchHistoryById("1")
        
        val result = dao.getSearchHistoryById("1")
        assertNull(result)
    }
    
    @Test
    fun deleteAllSearchHistory() = runTest {
        val search1 = createTestSearch("1", "query1")
        val search2 = createTestSearch("2", "query2")
        
        dao.insertSearchHistories(listOf(search1, search2))
        dao.deleteAllSearchHistory()
        
        val result = dao.getAllSearchHistory().first()
        assertEquals(0, result.size)
    }
    
    @Test
    fun deleteSearchHistoryOlderThan() = runTest {
        val oldSearch = createTestSearch("1", "old query", timestamp = Date(System.currentTimeMillis() - 86400000 * 2))
        val recentSearch = createTestSearch("2", "recent query", timestamp = Date(System.currentTimeMillis() - 86400000))
        
        dao.insertSearchHistories(listOf(oldSearch, recentSearch))
        dao.deleteSearchHistoryOlderThan(System.currentTimeMillis() - 86400000)
        
        val result = dao.getAllSearchHistory().first()
        assertEquals(1, result.size)
        assertEquals("2", result[0].id)
    }
    
    @Test
    fun insertSearchHistoryWithConflict() = runTest {
        val search = createTestSearch("1", "kotlin")
        
        dao.insertSearchHistory(search)
        
        val duplicate = search.copy(query = "android")
        val result = dao.insertSearchHistory(duplicate)
        
        assertEquals(-1L, result)
        
        val dbSearch = dao.getSearchHistoryById("1")
        assertEquals("kotlin", dbSearch?.query)
    }
    
    private fun createTestSearch(
        id: String,
        query: String,
        timestamp: Date = Date()
    ): SearchHistoryEntity {
        return SearchHistoryEntity(
            id = id,
            query = query,
            timestamp = timestamp
        )
    }
}
