package wang.yaojia.webterm.screens

import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material3.Button
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.ModalBottomSheet
import androidx.compose.material3.SheetState
import androidx.compose.material3.Text
import androidx.compose.material3.rememberModalBottomSheetState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import kotlinx.coroutines.launch
import wang.yaojia.webterm.designsystem.Spacing
import wang.yaojia.webterm.designsystem.WebTermTheme
import wang.yaojia.webterm.viewmodels.TimelineCopy
import wang.yaojia.webterm.viewmodels.TimelinePhase
import wang.yaojia.webterm.viewmodels.TimelineRow
import wang.yaojia.webterm.viewmodels.TimelineViewModel

/** Min sheet body height so the loading / empty / error states don't collapse to a sliver. */
private val SHEET_MIN_HEIGHT = 240.dp

/** Fixed width for the glyph column so the HH:mm / label columns line up across rows. */
private val GLYPH_COLUMN_WIDTH = 28.dp

/**
 * # TimelineSheet (A28) — the full activity-timeline drill-down, as a Material3 [ModalBottomSheet].
 *
 * Opened by the away-digest「展开」affordance (`AwayDigestView.onExpand`, A22 seam): the caller builds a
 * fresh [TimelineViewModel] via [TimelineViewModel.forSession] (id + `ApiClient::events`), shows this
 * sheet, and passes [onDismiss] to tear it down. A fresh VM per presentation → exactly one fetch, kicked
 * by the [LaunchedEffect] below.
 *
 * Rows mirror the web timeline panel — "HH:mm · glyph · label", newest-first, capped (the reducer lives
 * in [TimelineViewModel]). The label is **untrusted server text**: rendered as an INERT [Text], single
 * line, no linkify/markdown (§8). The Compose surface here is DEVICE-QA (plan §7); the fetch/present/
 * mapping cores are the JVM-tested logic in [TimelineViewModel].
 */
@OptIn(ExperimentalMaterial3Api::class)
@Composable
public fun TimelineSheet(
    viewModel: TimelineViewModel,
    onDismiss: () -> Unit,
    modifier: Modifier = Modifier,
    sheetState: SheetState = rememberModalBottomSheetState(),
) {
    val phase by viewModel.phase.collectAsStateWithLifecycle()
    val scope = rememberCoroutineScope()

    // One fetch per presentation (fresh VM ⇒ fresh load); the「重试」button re-runs the same path.
    LaunchedEffect(viewModel) { viewModel.load() }

    ModalBottomSheet(onDismissRequest = onDismiss, sheetState = sheetState, modifier = modifier) {
        TimelineSheetContent(
            phase = phase,
            onRetry = { scope.launch { viewModel.load() } },
        )
    }
}

/** The phase switch + rows — split out so it previews without a live [ModalBottomSheet] host. */
@Composable
private fun TimelineSheetContent(
    phase: TimelinePhase,
    onRetry: () -> Unit,
    modifier: Modifier = Modifier,
) {
    Column(
        modifier = modifier
            .fillMaxWidth()
            .heightIn(min = SHEET_MIN_HEIGHT)
            .padding(horizontal = Spacing.lg16),
        verticalArrangement = Arrangement.spacedBy(Spacing.sm8),
    ) {
        Text(
            text = TimelineCopy.TITLE,
            style = MaterialTheme.typography.titleMedium,
            color = MaterialTheme.colorScheme.onSurface,
            modifier = Modifier.padding(vertical = Spacing.sm8),
        )
        when (phase) {
            is TimelinePhase.Loading -> CenteredBox { CircularProgressIndicator() }
            is TimelinePhase.Empty -> EmptyState()
            is TimelinePhase.Failed -> FailedState(onRetry = onRetry)
            is TimelinePhase.Loaded -> TimelineRowList(rows = phase.rows)
        }
    }
}

@Composable
private fun TimelineRowList(rows: List<TimelineRow>) {
    LazyColumn(
        modifier = Modifier.fillMaxWidth(),
        verticalArrangement = Arrangement.spacedBy(Spacing.xs4),
    ) {
        items(items = rows, key = { it.key }) { row -> TimelineRowView(row) }
    }
}

@Composable
private fun TimelineRowView(row: TimelineRow) {
    // Unknown/future class → theme-adaptive secondary text (colorSpec == null; §8 fallback).
    val glyphColor = row.colorSpec?.let { Color(it) } ?: MaterialTheme.colorScheme.onSurfaceVariant
    Row(
        modifier = Modifier
            .fillMaxWidth()
            .padding(vertical = Spacing.xs2),
        horizontalArrangement = Arrangement.spacedBy(Spacing.md12),
        verticalAlignment = Alignment.CenterVertically,
    ) {
        // Monospace so the HH:mm column lines up across rows (tabular direction).
        Text(
            text = row.time,
            style = MaterialTheme.typography.bodySmall,
            fontFamily = FontFamily.Monospace,
            color = MaterialTheme.colorScheme.onSurfaceVariant,
        )
        Text(
            text = row.glyph,
            style = MaterialTheme.typography.bodyMedium,
            color = glyphColor,
            modifier = Modifier.width(GLYPH_COLUMN_WIDTH),
        )
        // INERT: server-derived phrase rendered verbatim, hard single-line truncation (§8).
        Text(
            text = row.label,
            style = MaterialTheme.typography.bodyMedium,
            color = MaterialTheme.colorScheme.onSurface,
            maxLines = 1,
            overflow = TextOverflow.Ellipsis,
        )
    }
}

@Composable
private fun EmptyState() {
    Column(
        modifier = Modifier
            .fillMaxWidth()
            .padding(vertical = Spacing.xl20),
        horizontalAlignment = Alignment.CenterHorizontally,
        verticalArrangement = Arrangement.spacedBy(Spacing.xs4),
    ) {
        Text(
            text = TimelineCopy.EMPTY_TITLE,
            style = MaterialTheme.typography.titleSmall,
            color = MaterialTheme.colorScheme.onSurface,
        )
        Text(
            text = TimelineCopy.EMPTY_DETAIL,
            style = MaterialTheme.typography.bodySmall,
            color = MaterialTheme.colorScheme.onSurfaceVariant,
        )
    }
}

@Composable
private fun FailedState(onRetry: () -> Unit) {
    Column(
        modifier = Modifier
            .fillMaxWidth()
            .padding(vertical = Spacing.xl20),
        horizontalAlignment = Alignment.CenterHorizontally,
        verticalArrangement = Arrangement.spacedBy(Spacing.sm8),
    ) {
        Text(
            text = TimelineCopy.LOAD_FAILED,
            style = MaterialTheme.typography.titleSmall,
            color = MaterialTheme.colorScheme.onSurface,
        )
        Text(
            text = TimelineCopy.LOAD_FAILED_DETAIL,
            style = MaterialTheme.typography.bodySmall,
            color = MaterialTheme.colorScheme.onSurfaceVariant,
        )
        Button(onClick = onRetry) { Text(text = TimelineCopy.RETRY) }
    }
}

@Composable
private fun CenteredBox(content: @Composable () -> Unit) {
    Box(
        modifier = Modifier
            .fillMaxWidth()
            .padding(vertical = Spacing.xl20),
        contentAlignment = Alignment.Center,
    ) { content() }
}

// ── Preview ─────────────────────────────────────────────────────────────────────

@Preview(name = "TimelineSheet · loaded")
@Composable
private fun TimelineSheetLoadedPreview() {
    WebTermTheme {
        TimelineSheetContent(
            phase = TimelinePhase.Loaded(
                listOf(
                    TimelineRow(0, "13:05", "✓", 0xFF46D07FL, "finished"),
                    TimelineRow(1, "13:04", "⏳", 0xFFF5B14CL, "waiting for approval"),
                    TimelineRow(2, "13:03", "🔧", 0xFF5E9EFFL, "ran Bash"),
                ),
            ),
            onRetry = {},
        )
    }
}

@Preview(name = "TimelineSheet · empty")
@Composable
private fun TimelineSheetEmptyPreview() {
    WebTermTheme { TimelineSheetContent(phase = TimelinePhase.Empty, onRetry = {}) }
}
