@file:OptIn(ExperimentalMaterial3AdaptiveApi::class)

package wang.yaojia.webterm.screens

import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.Text
import androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi
import androidx.compose.material3.adaptive.currentWindowAdaptiveInfo
import androidx.compose.material3.adaptive.layout.AnimatedPane
import androidx.compose.material3.adaptive.layout.ListDetailPaneScaffold
import androidx.compose.material3.adaptive.navigation.rememberListDetailPaneScaffoldNavigator
import androidx.compose.material3.adaptive.navigationsuite.NavigationSuiteScaffold
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.window.core.layout.WindowSizeClass
import wang.yaojia.webterm.nav.LayoutMode
import wang.yaojia.webterm.nav.LayoutPolicy
import wang.yaojia.webterm.nav.WindowWidth

/**
 * # AdaptiveHome (A26) — the Material 3 Adaptive shell (iPad-equivalent split).
 *
 * The Android analogue of iOS's adaptive root: a `NavigationSuiteScaffold` (bottom-bar → nav-rail →
 * nav-drawer as the window grows) wrapping a layout that switches on [LayoutPolicy]:
 *
 *  - [LayoutMode.LIST_DETAIL] (medium/expanded) → a `ListDetailPaneScaffold` showing the session
 *    **list pane** and the terminal **detail pane** side by side (plan §"iPad-equivalent").
 *  - [LayoutMode.STACK] (compact) → a single pane rendering the list; the outer `NavGraph` pushes the
 *    terminal on top (the existing phone flow). Full inter-screen routing is a later app-assembly step
 *    (A32), so this shell only exposes the list / detail **slots** — it does NOT hard-wire every screen.
 *
 * The size-class decision is read ONCE, here, through [LayoutPolicy] (mirroring iOS's single decision
 * point); the thin `WindowSizeClass → WindowWidth` extraction is [toWindowWidth]. Everything else — pane
 * rendering, navigation-suite chrome, animations — is device-QA (plan §7).
 *
 * @param listPane the session list / chooser (A20 `SessionListScreen`), shown in the list pane / stack.
 * @param detailPane the terminal / detail (A21 `TerminalScreen`, or an empty placeholder), shown in the
 *   detail pane when [LayoutMode.LIST_DETAIL]. Ignored in [LayoutMode.STACK] (the NavGraph pushes it).
 * @param destinations the top-level navigation entries rendered by the navigation suite.
 * @param selectedDestination index of the active [destinations] entry.
 * @param onSelectDestination invoked when a navigation-suite entry is tapped.
 * @param windowSizeClass the current window size class; defaults to `currentWindowAdaptiveInfo()` and is
 *   injectable so the shell can be driven from a preview / test harness.
 */
@Composable
public fun AdaptiveHome(
    listPane: @Composable () -> Unit,
    detailPane: @Composable () -> Unit,
    modifier: Modifier = Modifier,
    destinations: List<AdaptiveDestination> = DEFAULT_DESTINATIONS,
    selectedDestination: Int = 0,
    onSelectDestination: (Int) -> Unit = {},
    windowSizeClass: WindowSizeClass = currentWindowAdaptiveInfo().windowSizeClass,
) {
    val mode = LayoutPolicy.mode(windowSizeClass.toWindowWidth())

    NavigationSuiteScaffold(
        navigationSuiteItems = {
            destinations.forEachIndexed { index, destination ->
                item(
                    selected = index == selectedDestination,
                    onClick = { onSelectDestination(index) },
                    icon = { Text(destination.glyph) },
                    label = { Text(destination.label) },
                )
            }
        },
        modifier = modifier,
    ) {
        when (mode) {
            LayoutMode.LIST_DETAIL -> ListDetailShell(listPane = listPane, detailPane = detailPane)
            // Compact: single pane. The NavGraph (A32) owns list→detail push, so the shell renders only
            // the list here — it must NOT hard-wire the terminal into the compact path.
            LayoutMode.STACK -> Box(modifier = Modifier.fillMaxSize()) { listPane() }
        }
    }
}

/**
 * The side-by-side pane composition. Uses the Material 3 Adaptive `ListDetailPaneScaffold` driven by a
 * remembered navigator, so pane visibility / expansion follows the adaptive directive. Each pane is an
 * `AnimatedPane` so show/hide transitions are stock.
 */
@Composable
private fun ListDetailShell(
    listPane: @Composable () -> Unit,
    detailPane: @Composable () -> Unit,
) {
    val navigator = rememberListDetailPaneScaffoldNavigator()
    ListDetailPaneScaffold(
        navigator.scaffoldDirective,
        navigator.scaffoldValue,
        listPane = { AnimatedPane { listPane() } },
        detailPane = { AnimatedPane { detailPane() } },
        modifier = Modifier.fillMaxSize(),
    )
}

/** One top-level navigation-suite entry: a glyph + a label. Glyph is a plain string to avoid a
 *  material-icons dependency (not in the `:app` Compose bundle — same rationale as the host menu). */
public data class AdaptiveDestination(val label: String, val glyph: String)

/** The default navigation-suite entries (sessions / projects) — a thin, replaceable default. */
public val DEFAULT_DESTINATIONS: List<AdaptiveDestination> = listOf(
    AdaptiveDestination(label = "会话", glyph = "▤"),
    AdaptiveDestination(label = "项目", glyph = "▦"),
)

/**
 * The thin, Compose-side `WindowSizeClass → WindowWidth` extraction (kept OUT of the pure
 * [LayoutPolicy] so the decision stays JVM-testable). Uses the modern `isWidthAtLeastBreakpoint`
 * predicate rather than the deprecated `windowWidthSizeClass` accessor: EXPANDED (≥840dp) → medium
 * (≥600dp) → compact, matching the Material width breakpoints.
 */
private fun WindowSizeClass.toWindowWidth(): WindowWidth = when {
    isWidthAtLeastBreakpoint(WindowSizeClass.WIDTH_DP_EXPANDED_LOWER_BOUND) -> WindowWidth.EXPANDED
    isWidthAtLeastBreakpoint(WindowSizeClass.WIDTH_DP_MEDIUM_LOWER_BOUND) -> WindowWidth.MEDIUM
    else -> WindowWidth.COMPACT
}
