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.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
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.CircularProgressIndicator
import androidx.compose.material3.FilterChip
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontWeight
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 wang.yaojia.webterm.designsystem.Spacing
import wang.yaojia.webterm.designsystem.Stroke
import wang.yaojia.webterm.designsystem.WebTermColors
import wang.yaojia.webterm.designsystem.WebTermTheme
import wang.yaojia.webterm.designsystem.WebTermType
import wang.yaojia.webterm.viewmodels.DiffBinaryRow
import wang.yaojia.webterm.viewmodels.DiffFileHeaderRow
import wang.yaojia.webterm.viewmodels.DiffHunkHeaderRow
import wang.yaojia.webterm.viewmodels.DiffLineKind
import wang.yaojia.webterm.viewmodels.DiffLineRow
import wang.yaojia.webterm.viewmodels.DiffPhase
import wang.yaojia.webterm.viewmodels.DiffRow
import wang.yaojia.webterm.viewmodels.DiffUiState
import wang.yaojia.webterm.viewmodels.DiffViewModel
import wang.yaojia.webterm.viewmodels.DiffWriteBanner

/**
 * # DiffScreen (A24 + W5) — the git-diff viewer with base-compare + git-write.
 *
 * Renders the presenter's flattened files→hunks→lines list in a `LazyColumn`, with a Working/Staged
 * toggle (hidden in base mode), a **base-rev** input (a third mode), per-file **Stage/Unstage** buttons
 * (working/staged mode only), a **commit** message field + **Commit** / **Push** buttons, and a result
 * **banner**. Every server-derived string (paths, hunk headers, code lines, git error messages) is
 * rendered as **inert [Text]** — never `ClickableText`/autolink/markdown (plan §8). Interaction is
 * device-QA (plan §7).
 */
@Composable
public fun DiffScreen(
    state: DiffUiState,
    onSelectStaged: (Boolean) -> Unit,
    modifier: Modifier = Modifier,
    onRefresh: () -> Unit = {},
    onBack: (() -> Unit)? = null,
    onSetBase: (String?) -> Unit = {},
    onToggleStage: (String, Boolean) -> Unit = { _, _ -> },
    onCommit: (String) -> Unit = {},
    onPush: () -> Unit = {},
    onDismissBanner: () -> Unit = {},
) {
    Surface(modifier = modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background) {
        Column(modifier = Modifier.fillMaxSize()) {
            DiffHeader(state = state, onSelectStaged = onSelectStaged, onBack = onBack, onSetBase = onSetBase)
            if (state.truncated) DiffNotice("Diff truncated — too large to display fully.")
            state.writeBanner?.let { WriteBanner(it, onDismissBanner) }
            HorizontalDivider(color = MaterialTheme.colorScheme.outline, thickness = Stroke.hairline)
            Box(modifier = Modifier.weight(1f).fillMaxWidth()) {
                when (state.phase) {
                    DiffPhase.IDLE, DiffPhase.LOADING -> CenteredContent { CircularProgressIndicator() }
                    DiffPhase.EMPTY -> CenteredMessage("No changes")
                    DiffPhase.ERROR -> DiffError(onRetry = onRefresh)
                    DiffPhase.LOADED -> DiffList(rows = state.rows, writeEnabled = state.writeEnabled, staged = state.staged, onToggleStage = onToggleStage)
                }
            }
            if (state.writeEnabled) {
                HorizontalDivider(color = MaterialTheme.colorScheme.outline, thickness = Stroke.hairline)
                CommitBar(writing = state.writing, onCommit = onCommit, onPush = onPush)
            }
        }
    }
}

/**
 * Stateful entry point: binds [viewModel] to a lifecycle scope, collects its state, and wires the
 * toggle/refresh/base/git-write callbacks. The nav layer supplies the already-constructed presenter.
 */
@Composable
public fun DiffScreen(
    viewModel: DiffViewModel,
    modifier: Modifier = Modifier,
    onBack: (() -> Unit)? = null,
) {
    val state by viewModel.uiState.collectAsStateWithLifecycle()
    LaunchedEffect(viewModel) { viewModel.bind(this) }
    DiffScreen(
        state = state,
        onSelectStaged = viewModel::selectStaged,
        modifier = modifier,
        onRefresh = viewModel::refresh,
        onBack = onBack,
        onSetBase = viewModel::setBase,
        onToggleStage = viewModel::toggleStage,
        onCommit = viewModel::commit,
        onPush = viewModel::push,
        onDismissBanner = viewModel::clearWriteBanner,
    )
}

@Composable
private fun DiffHeader(
    state: DiffUiState,
    onSelectStaged: (Boolean) -> Unit,
    onBack: (() -> Unit)?,
    onSetBase: (String?) -> Unit,
) {
    var baseInput by remember(state.base) { mutableStateOf(state.base ?: "") }
    Column(modifier = Modifier.fillMaxWidth().padding(horizontal = Spacing.md12, vertical = Spacing.sm8)) {
        Row(verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(Spacing.sm8)) {
            if (onBack != null) TextButton(onClick = onBack) { Text("Back") }
            Text(text = "Diff", style = MaterialTheme.typography.titleMedium, color = MaterialTheme.colorScheme.onBackground)
            Spacer(modifier = Modifier.width(Spacing.sm8))
            if (state.base == null) {
                // Working/Staged toggle is suppressed in base mode (server ignores staged then).
                FilterChip(selected = !state.staged, onClick = { onSelectStaged(false) }, label = { Text("Working") })
                FilterChip(selected = state.staged, onClick = { onSelectStaged(true) }, label = { Text("Staged") })
            } else {
                Text(text = "vs ${state.base}", style = WebTermType.metaMono, color = MaterialTheme.colorScheme.primary)
            }
        }
        Row(verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(Spacing.sm8), modifier = Modifier.padding(top = Spacing.xs4)) {
            OutlinedTextField(
                value = baseInput,
                onValueChange = { baseInput = it },
                label = { Text("对比基点 (base rev)") },
                singleLine = true,
                modifier = Modifier.weight(1f),
            )
            OutlinedButton(onClick = { onSetBase(baseInput.takeIf { it.isNotBlank() }) }) { Text("对比") }
            if (state.base != null) OutlinedButton(onClick = { baseInput = ""; onSetBase(null) }) { Text("清除") }
        }
    }
}

@Composable
private fun DiffList(
    rows: List<DiffRow>,
    writeEnabled: Boolean,
    staged: Boolean,
    onToggleStage: (String, Boolean) -> Unit,
) {
    LazyColumn(modifier = Modifier.fillMaxSize()) {
        items(items = rows, key = { it.id }) { row ->
            when (row) {
                is DiffFileHeaderRow -> FileHeader(row, writeEnabled = writeEnabled, staged = staged, onToggleStage = onToggleStage)
                is DiffHunkHeaderRow -> DiffText(row.header, MaterialTheme.colorScheme.primary)
                is DiffLineRow -> DiffText(markerFor(row.kind) + row.text, lineColor(row.kind))
                is DiffBinaryRow -> DiffText("Binary file", MaterialTheme.colorScheme.onSurfaceVariant)
            }
        }
    }
}

@Composable
private fun FileHeader(
    row: DiffFileHeaderRow,
    writeEnabled: Boolean,
    staged: Boolean,
    onToggleStage: (String, Boolean) -> Unit,
) {
    Row(
        modifier = Modifier.fillMaxWidth().padding(horizontal = Spacing.md12, vertical = Spacing.sm8),
        horizontalArrangement = Arrangement.spacedBy(Spacing.sm8),
        verticalAlignment = Alignment.CenterVertically,
    ) {
        Text(
            text = row.path,
            style = WebTermType.monoTabular(13),
            fontWeight = FontWeight.SemiBold,
            color = MaterialTheme.colorScheme.onSurface,
            maxLines = 2,
            overflow = TextOverflow.Ellipsis,
            modifier = Modifier.weight(1f),
        )
        Text("+${row.added}", style = WebTermType.metaMono, color = WebTermColors.statusWorking)
        Text("-${row.removed}", style = WebTermType.metaMono, color = WebTermColors.statusStuck)
        if (writeEnabled) {
            // In staged view we offer Unstage; in working view we offer Stage.
            TextButton(onClick = { onToggleStage(row.stagePath, !staged) }) { Text(if (staged) "取消暂存" else "暂存") }
        }
    }
}

@Composable
private fun CommitBar(writing: Boolean, onCommit: (String) -> Unit, onPush: () -> Unit) {
    var message by remember { mutableStateOf("") }
    Column(modifier = Modifier.fillMaxWidth().padding(horizontal = Spacing.md12, vertical = Spacing.sm8), verticalArrangement = Arrangement.spacedBy(Spacing.xs4)) {
        OutlinedTextField(
            value = message,
            onValueChange = { message = it },
            label = { Text("提交信息") },
            singleLine = true,
            enabled = !writing,
            modifier = Modifier.fillMaxWidth(),
        )
        Row(horizontalArrangement = Arrangement.spacedBy(Spacing.sm8)) {
            OutlinedButton(enabled = !writing, onClick = { onCommit(message); message = "" }) { Text("提交") }
            OutlinedButton(enabled = !writing, onClick = onPush) { Text("推送") }
        }
    }
}

@Composable
private fun WriteBanner(banner: DiffWriteBanner, onDismiss: () -> Unit) {
    val color = if (banner.isError) WebTermColors.statusStuck else WebTermColors.statusWorking
    Row(
        modifier = Modifier.fillMaxWidth().padding(horizontal = Spacing.md12, vertical = Spacing.xs4),
        verticalAlignment = Alignment.CenterVertically,
        horizontalArrangement = Arrangement.spacedBy(Spacing.sm8),
    ) {
        Text(text = banner.message, style = WebTermType.metaMono, color = color, modifier = Modifier.weight(1f))
        TextButton(onClick = onDismiss) { Text("×") }
    }
}

/** One inert monospaced diff line. No linkify/markdown — a hostile diff renders as literal text (§8). */
@Composable
private fun DiffText(text: String, color: Color) {
    Text(
        text = text,
        style = WebTermType.monoTabular(13),
        color = color,
        softWrap = false,
        maxLines = 1,
        overflow = TextOverflow.Clip,
        modifier = Modifier.fillMaxWidth().padding(horizontal = Spacing.md12, vertical = 1.dp),
    )
}

@Composable
private fun DiffError(onRetry: () -> Unit) {
    CenteredContent {
        Column(horizontalAlignment = Alignment.CenterHorizontally) {
            Text("Failed to load diff.", color = MaterialTheme.colorScheme.onBackground)
            TextButton(onClick = onRetry) { Text("Retry") }
        }
    }
}

@Composable
private fun DiffNotice(message: String) {
    Text(
        text = message,
        style = MaterialTheme.typography.bodySmall,
        color = MaterialTheme.colorScheme.onSurfaceVariant,
        modifier = Modifier.fillMaxWidth().padding(horizontal = Spacing.md12, vertical = Spacing.xs4),
    )
}

@Composable
private fun CenteredMessage(message: String) {
    CenteredContent { Text(message, color = MaterialTheme.colorScheme.onSurfaceVariant) }
}

@Composable
private fun CenteredContent(content: @Composable () -> Unit) {
    Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { content() }
}

/** A13 colour token per line kind (added → green, removed → red; context/hunk/meta from the scheme). */
@Composable
private fun lineColor(kind: DiffLineKind): Color = when (kind) {
    DiffLineKind.ADDED -> WebTermColors.statusWorking
    DiffLineKind.REMOVED -> WebTermColors.statusStuck
    DiffLineKind.META -> MaterialTheme.colorScheme.onSurfaceVariant
    DiffLineKind.HUNK -> MaterialTheme.colorScheme.primary
    DiffLineKind.CONTEXT -> MaterialTheme.colorScheme.onSurface
}

/** The gutter marker the server stripped off; re-added purely for readability (kept 1-col wide). */
private fun markerFor(kind: DiffLineKind): String = when (kind) {
    DiffLineKind.ADDED -> "+ "
    DiffLineKind.REMOVED -> "- "
    else -> "  "
}

@Preview(name = "DiffScreen — loaded")
@Composable
private fun DiffScreenPreview() {
    val rows = listOf<DiffRow>(
        DiffFileHeaderRow(0, "src/app/Main.kt", "src/app/Main.kt", "modified", added = 2, removed = 1),
        DiffHunkHeaderRow(1, "@@ -1,3 +1,4 @@"),
        DiffLineRow(2, DiffLineKind.CONTEXT, "fun main() {"),
        DiffLineRow(3, DiffLineKind.REMOVED, "  println(\"old\")"),
        DiffLineRow(4, DiffLineKind.ADDED, "  println(\"new\")"),
    )
    WebTermTheme {
        DiffScreen(
            state = DiffUiState(staged = false, phase = DiffPhase.LOADED, rows = rows, truncated = true, canWrite = true),
            onSelectStaged = {},
        )
    }
}
