package theorycrafter.ui.fiteditor

import androidx.compose.ui.input.key.Key
import androidx.compose.ui.test.*
import theorycrafter.*
import theorycrafter.TheorycrafterContext.eveData
import theorycrafter.TheorycrafterContext.fits
import theorycrafter.ui.FitListPanel
import theorycrafter.ui.MainWindowContent
import kotlin.test.Ignore
import kotlin.test.Test
import kotlin.test.assertEquals

/**
 * Tests of the fit list.
 */
class FitListTest: TheorycrafterTest() {


    /**
     * Test that the search re-runs and finds the new fit if it is added while a query has already been typed.
     */
    @Test
    fun searchFindsNewFitImmediately() = runBlockingTest {
        rule.setApplicationContent {
            FitListPanel(
                onRenameFit = { _, _ -> },
                onCreateNewFit = { },
                onImportFitFromClipboard = { },
                onImportFitsFromXmlFile = { },
                onImportFitsFromPyfa = { },
                onExportFitsToXmlFile = { },
                onDeleteFits = { },
                displayedFitHandle = null
            )
        }

        val shipType = eveData.shipType("Caracal")
        val fitName = "Test ${shipType.name}"

        rule.onNode(Nodes.FitList.SearchField).performTextInput(fitName)
        rule.onNode(Nodes.FitList.FitList).assertDoesNotExist()

        fits.addEmpty(shipType, fitName)

        rule.onNode(Nodes.FitList.FitList).assertExists()
        rule.onNode(Nodes.FitList.Item.Item).apply {
            assertExists()
            onChildren()
        }
        rule.onSubtreeRootedAt(Nodes.FitList.Item.Item).assertAny(hasText(fitName))
    }


    /**
     * Tests that showing a fit from the fit list moves the focus to the fit editor.
     */
    @OptIn(ExperimentalTestApi::class)
    @Test
    @Ignore  // Enable after removing items in SimpleGrid/LazyColumnExt being focused.
    fun showingFitMovesFocusToEditor() = runBlockingTest {
        rule.setApplicationContent {
            MainWindowContent()
        }

        val shipType = eveData.shipType("Omen")
        val fitName = "Test ${shipType.name}"
        fits.addEmpty(shipType, fitName)

        with(rule.onNode(Nodes.FitList.Item.Item)) {
            scrollToAndClick()
            press(Key.Enter)  // To show the fit
        }

        rule.waitUntilExactlyOneExists(Nodes.FitEditor.KeyEventsReceiver)
        rule.onSubtreeRootedAt(Nodes.FitEditor.KeyEventsReceiver).assertAny(isFocused())
    }


    /**
     * Tests creating and deleting a fit.
     */
    @OptIn(ExperimentalTestApi::class)
    @Test
    fun createAndDeleteFit() = runBlockingTest {
        rule.setApplicationContent {
            MainWindowContent()
        }

        val shipType = eveData.shipType("Caracal")
        val fitName = "Test ${shipType.name}"
        repeat(1) {  // Increase count to test race conditions
            fits.addEmpty(shipType, fitName)
            with(rule.onNode(Nodes.FitList.Item.Item)) {
                scrollToAndClick()
                press(Key.Enter)  // To show the fit
                rule.waitUntilExactlyOneExists(Nodes.FitEditor.KeyEventsReceiver)
                scrollToAndClick()  // Move focus back to the fit list
                press(Key.Delete)
            }
            rule.onNode(hasAnyAncestor(isDialog()) and hasText("Delete")).performClick()
            rule.onNode(Nodes.FitList.Item.Item).assertDoesNotExist()
        }
    }


    /**
     * Tests repiadly deleting many fits.
     */
    @Test
    fun rapidlyDeleteFits() = runBlockingTest {
        rule.setApplicationContent {
            MainWindowContent()
        }

        val count = 10

        val shipType = eveData.shipType("Caracal")
        repeat(count) {
            fits.addEmpty(shipType, "Test ${shipType.name} $it")
        }
        rule.waitForIdle()
        assertEquals(count, fits.handles.size)

        rule.onAllNodes(Nodes.FitList.Item.Item).onLast().scrollToAndClick()

        repeat(count) {
            rule.onNode(isFocused()).press(Key.Delete)
            rule.onNode(isDialog()).press(Key.Enter)
        }
        rule.waitForIdle()

        assertEquals(0, fits.handles.size)
        rule.onNode(Nodes.FitList.Item.Item).assertDoesNotExist()
    }


}