package com.yorvana.testsupport

import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.By
import androidx.test.uiautomator.UiDevice
import androidx.test.uiautomator.Until

object SystemPicker {
    private const val TIMEOUT_MILLIS = 10_000L
    private const val SHORT_TIMEOUT_MILLIS = 1_500L

    fun pickFile(
        device: UiDevice,
        displayName: String,
    ) {
        device.waitForIdle()
        Permissions.acceptIfShown(device)

        // Try multiple matching strategies for the file
        // To handle potential truncation in grid views, we extract a prefix if the display name is long.
        val prefix = if (displayName.length > 25) displayName.substring(0, 24) else displayName
        val selectors =
            listOf(
                By.text(displayName),
                By.textContains(displayName),
                By.descContains(displayName),
                By.textContains(displayName.substringBeforeLast(".")), // Try without extension
                By.textContains(prefix),
                By.descContains(prefix),
            )

        var obj = findWithExhaustiveSearch(device, selectors)

        if (obj == null) {
            error("System picker item not found: $displayName")
        }

        obj.click()
        device.waitForIdle()
    }

    private fun findWithExhaustiveSearch(
        device: UiDevice,
        selectors: List<androidx.test.uiautomator.BySelector>,
    ): androidx.test.uiautomator.UiObject2? {
        // 1. Current view
        for (selector in selectors) {
            val found = device.wait(Until.findObject(selector), SHORT_TIMEOUT_MILLIS)
            if (found != null) return found
        }

        // 2. Scroll down multiple times
        for (i in 1..3) {
            device.executeShellCommand("input swipe 500 1500 500 200")
            Thread.sleep(1000)
            for (selector in selectors) {
                val found = device.wait(Until.findObject(selector), SHORT_TIMEOUT_MILLIS)
                if (found != null) return found
            }
        }

        // 3. Try Roots
        val roots = listOf("Downloads", "Files", "Internal storage", "Recent")
        for (rootName in roots) {
            try {
                navigateToRoots(device)
                val rootObj =
                    device.wait(Until.findObject(By.text(rootName)), TIMEOUT_MILLIS)
                        ?: device.wait(Until.findObject(By.textContains(rootName)), TIMEOUT_MILLIS)

                if (rootObj != null) {
                    rootObj.click()
                    device.waitForIdle()
                    for (selector in selectors) {
                        val found = device.wait(Until.findObject(selector), TIMEOUT_MILLIS)
                        if (found != null) return found
                    }
                    // Try one scroll in the root
                    device.executeShellCommand("input swipe 500 1500 500 200")
                    for (selector in selectors) {
                        val found = device.wait(Until.findObject(selector), SHORT_TIMEOUT_MILLIS)
                        if (found != null) return found
                    }
                }
            } catch (_: Exception) {
            }
        }

        return null
    }

    private fun navigateToRoots(device: UiDevice) {
        val drawer =
            device.wait(Until.findObject(By.descContains("Show roots")), SHORT_TIMEOUT_MILLIS)
                ?: device.wait(Until.findObject(By.descContains("Show")), SHORT_TIMEOUT_MILLIS)
        drawer?.click()
    }

    fun pickFolder(
        device: UiDevice,
        displayName: String,
    ) {
        clickText(device, displayName)
        if (!confirmFolder(device)) {
            error("DocumentsUI folder confirm action not found")
        }
    }

    fun createAndPickFolder(
        device: UiDevice,
        displayName: String,
    ) {
        device.waitForIdle()
        Permissions.acceptIfShown(device)

        // Navigate to a root supporting folder creation if the new folder action isn't visible
        var newFolder = findNewFolderAction(device)
        if (newFolder == null) {
            val more =
                device.wait(Until.findObject(By.descContains("More options")), SHORT_TIMEOUT_MILLIS)
                    ?: device.wait(Until.findObject(By.descContains("More")), SHORT_TIMEOUT_MILLIS)
            if (more != null) {
                more.click()
                newFolder = findNewFolderAction(device)
                if (newFolder == null) {
                    device.pressBack() // Close more options popup
                }
            }
        }

        if (newFolder == null) {
            try {
                navigateToRoots(device)
                val targetRoot =
                    device.wait(Until.findObject(By.text("Downloads")), TIMEOUT_MILLIS)
                        ?: device.wait(Until.findObject(By.textContains("Downloads")), TIMEOUT_MILLIS)
                        ?: device.wait(Until.findObject(By.text("Internal storage")), TIMEOUT_MILLIS)
                        ?: device.wait(Until.findObject(By.textContains("Internal storage")), TIMEOUT_MILLIS)
                targetRoot?.click()
                device.waitForIdle()
            } catch (_: Exception) {
            }
        }

        newFolder =
            findNewFolderAction(device)
                ?: run {
                    val more =
                        device.wait(Until.findObject(By.descContains("More options")), TIMEOUT_MILLIS)
                            ?: device.wait(Until.findObject(By.descContains("More")), TIMEOUT_MILLIS)
                    more?.click()
                    findNewFolderAction(device)
                }
                ?: error("DocumentsUI new-folder action not found")
        newFolder.click()
        val input =
            device.wait(Until.findObject(By.clazz("android.widget.EditText")), TIMEOUT_MILLIS)
                ?: error("DocumentsUI new-folder input not found")
        input.setText(displayName)

        // Click OK on the dialog
        val ok =
            device.wait(Until.findObject(By.text("OK")), SHORT_TIMEOUT_MILLIS)
                ?: device.wait(Until.findObject(By.res("android:id/button1")), SHORT_TIMEOUT_MILLIS)
        ok?.click()

        device.waitForIdle()

        if (!confirmFolder(device, allowOverflowRetry = false)) {
            // Try to find the folder in the list and enter it
            try {
                clickText(device, displayName)
                if (!confirmFolder(device)) {
                    error("DocumentsUI folder confirm action not found after entering folder")
                }
            } catch (e: Exception) {
                error("DocumentsUI folder confirm action not found: ${e.message}")
            }
        }
    }

    private fun confirmFolder(
        device: UiDevice,
        allowOverflowRetry: Boolean = true,
    ): Boolean {
        device.waitForIdle()
        val confirm = findFolderConfirmAction(device)
        if (confirm == null) {
            if (!allowOverflowRetry) return false
            val overflowConfirm =
                findOverflowAction(device)?.let { overflow ->
                    overflow.click()
                    findFolderConfirmAction(device)
                }
            if (overflowConfirm != null) {
                overflowConfirm.click()
                Permissions.acceptIfShown(device)
                return waitForApp(device)
            }
            return false
        } else {
            confirm.click()
        }
        Permissions.acceptIfShown(device)
        return waitForApp(device)
    }

    private fun findNewFolderAction(device: UiDevice) =
        device.wait(Until.findObject(By.descContains("New folder")), SHORT_TIMEOUT_MILLIS)
            ?: device.wait(Until.findObject(By.descContains("Create folder")), SHORT_TIMEOUT_MILLIS)
            ?: device.wait(Until.findObject(By.textContains("New folder")), SHORT_TIMEOUT_MILLIS)
            ?: device.wait(Until.findObject(By.textContains("New Folder")), SHORT_TIMEOUT_MILLIS)
            ?: device.wait(Until.findObject(By.textContains("Create folder")), SHORT_TIMEOUT_MILLIS)
            ?: device.wait(Until.findObject(By.res("com.google.android.documentsui:id/menu_create_dir")), SHORT_TIMEOUT_MILLIS)
            ?: device.wait(Until.findObject(By.res("com.android.documentsui:id/menu_create_dir")), SHORT_TIMEOUT_MILLIS)

    private fun findOverflowAction(device: UiDevice) =
        device.wait(Until.findObject(By.descContains("More options")), SHORT_TIMEOUT_MILLIS)
            ?: device.wait(Until.findObject(By.descContains("More")), SHORT_TIMEOUT_MILLIS)

    private fun findFolderConfirmAction(device: UiDevice) =
        device.wait(Until.findObject(By.text("Use this folder")), TIMEOUT_MILLIS)
            ?: device.wait(Until.findObject(By.textContains("Use this folder")), SHORT_TIMEOUT_MILLIS)
            ?: device.wait(Until.findObject(By.textContains("USE THIS FOLDER")), SHORT_TIMEOUT_MILLIS)
            ?: device.wait(Until.findObject(By.descContains("Use this folder")), SHORT_TIMEOUT_MILLIS)
            ?: device.wait(Until.findObject(By.descContains("USE THIS FOLDER")), SHORT_TIMEOUT_MILLIS)
            ?: device.wait(Until.findObject(By.text("Select")), SHORT_TIMEOUT_MILLIS)
            ?: device.wait(Until.findObject(By.textContains("Select")), SHORT_TIMEOUT_MILLIS)
            ?: device.wait(Until.findObject(By.descContains("Select")), SHORT_TIMEOUT_MILLIS)
            ?: device.wait(Until.findObject(By.text("Choose")), SHORT_TIMEOUT_MILLIS)
            ?: device.wait(Until.findObject(By.text("Done")), SHORT_TIMEOUT_MILLIS)
            ?: device.wait(Until.findObject(By.res("android:id/button1")), SHORT_TIMEOUT_MILLIS)
            ?: device.wait(Until.findObject(By.res("com.google.android.documentsui:id/action_menu_select")), SHORT_TIMEOUT_MILLIS)
            ?: device.wait(Until.findObject(By.res("com.android.documentsui:id/action_menu_select")), SHORT_TIMEOUT_MILLIS)

    private fun waitForApp(device: UiDevice): Boolean {
        val packageName = InstrumentationRegistry.getInstrumentation().targetContext.packageName
        return device.wait(Until.hasObject(By.pkg(packageName)), TIMEOUT_MILLIS)
    }

    private fun clickTextOrNull(
        device: UiDevice,
        text: String,
    ) {
        try {
            clickText(device, text)
        } catch (_: Exception) {
        }
    }

    private fun clickText(
        device: UiDevice,
        text: String,
    ) {
        val selector = By.text(text)
        val obj =
            device.wait(Until.findObject(selector), TIMEOUT_MILLIS)
                ?: device.wait(Until.findObject(By.textContains(text)), TIMEOUT_MILLIS)
                ?: error("System picker item not found: $text")

        device.waitForIdle()
        try {
            obj.click()
        } catch (e: Exception) {
            // Retry once if stale or other transient issue
            device.wait(Until.findObject(By.text(text)), TIMEOUT_MILLIS)?.click()
                ?: throw e
        }
    }
}
