package com.tt.honeyDue.ui

import androidx.compose.ui.test.SemanticsNodeInteraction
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.junit4.ComposeTestRule
import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.test.onNodeWithText

/**
 * Base class for Android Compose UI test page objects.
 *
 * Mirrors `iosApp/HoneyDueUITests/PageObjects/BaseScreen.swift`: provides
 * condition-based waits so screen objects can interact with Compose nodes
 * without reaching for ad-hoc `Thread.sleep` calls.
 */
abstract class BaseScreen(protected val rule: ComposeTestRule) {

    /** Returns a node interaction for the given test tag (unmerged tree for testability). */
    protected fun tag(testTag: String): SemanticsNodeInteraction =
        rule.onNodeWithTag(testTag, useUnmergedTree = true)

    /** Returns a node interaction for the given display text. */
    protected fun text(value: String): SemanticsNodeInteraction =
        rule.onNodeWithText(value, useUnmergedTree = true)

    /** Waits until a node with [testTag] exists in the semantics tree. */
    protected fun waitFor(testTag: String, timeoutMs: Long = DEFAULT_TIMEOUT_MS) {
        rule.waitUntil(timeoutMs) {
            try {
                tag(testTag).assertExists()
                true
            } catch (e: AssertionError) {
                false
            }
        }
    }

    /** Waits until a node with the given visible [value] text exists. */
    protected fun waitForText(value: String, timeoutMs: Long = DEFAULT_TIMEOUT_MS) {
        rule.waitUntil(timeoutMs) {
            try {
                text(value).assertExists()
                true
            } catch (e: AssertionError) {
                false
            }
        }
    }

    /** Waits until a node with [testTag] is actually displayed (not just present). */
    protected fun waitForDisplayed(testTag: String, timeoutMs: Long = DEFAULT_TIMEOUT_MS) {
        rule.waitUntil(timeoutMs) {
            try {
                tag(testTag).assertIsDisplayed()
                true
            } catch (e: AssertionError) {
                false
            }
        }
    }

    /** Non-throwing existence check. */
    protected fun exists(testTag: String): Boolean = try {
        tag(testTag).assertExists()
        true
    } catch (e: AssertionError) {
        false
    }

    /** Non-throwing text existence check. */
    protected fun textExists(value: String): Boolean = try {
        text(value).assertExists()
        true
    } catch (e: AssertionError) {
        false
    }

    /** Subclasses report whether their screen is currently visible. */
    abstract fun isDisplayed(): Boolean

    companion object {
        const val DEFAULT_TIMEOUT_MS: Long = 10_000L
        const val SHORT_TIMEOUT_MS: Long = 5_000L
    }
}
