package wang.yaojia.webterm.screens

import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxWithConstraints
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.lazy.LazyColumn
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.MaterialTheme
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.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
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.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import kotlinx.coroutines.launch
import wang.yaojia.webterm.api.models.ProjectInfo
import wang.yaojia.webterm.designsystem.Spacing
import wang.yaojia.webterm.designsystem.WebTermCard
import wang.yaojia.webterm.designsystem.WebTermColors
import wang.yaojia.webterm.designsystem.WebTermTheme
import wang.yaojia.webterm.designsystem.WebTermType
import wang.yaojia.webterm.viewmodels.ProjectGroup
import wang.yaojia.webterm.viewmodels.ProjectGroupKind
import wang.yaojia.webterm.viewmodels.ProjectGrouping
import wang.yaojia.webterm.viewmodels.ProjectsCopy
import wang.yaojia.webterm.viewmodels.ProjectsUiState
import wang.yaojia.webterm.viewmodels.ProjectsViewModel

/**
 * # ProjectsScreen (A23) — the grouped Projects grid (mirrors web v0.6 / iOS `ProjectsScreen`).
 *
 * Renders the presenter's [ProjectsUiState.groups] as collapsible namespace sections of project cards.
 * Every server-derived string (name/path/branch) is rendered as **inert [Text]** — plain `Text`, never
 * `ClickableText`/`LinkAnnotation`/autolink/markdown — so a hostile project name/branch can't inject a
 * tappable link (plan §8). The regular-width grid column count comes from the pure [ProjectsGridLayout]
 * seam; A26 owns the full adaptive (ListDetail/NavigationSuite) layout. All Compose/grid/gesture
 * behaviour is device-QA (plan §7).
 *
 * @param onOpenDetail open the [ProjectDetailScreen] for a tapped project path.
 */
@Composable
public fun ProjectsScreen(
    viewModel: ProjectsViewModel,
    onOpenDetail: (String) -> Unit,
    modifier: Modifier = Modifier,
) {
    val state by viewModel.uiState.collectAsStateWithLifecycle()
    val scope = rememberCoroutineScope()
    // Load once on entry (prefs + projects); the screen supplies the lifecycle scope for toggles.
    LaunchedEffect(viewModel) { viewModel.load() }
    ProjectsScreen(
        state = state,
        onSearchChange = viewModel::onSearchChange,
        onToggleFavourite = { path -> scope.launch { viewModel.toggleFavourite(path) } },
        onToggleCollapsed = { key -> scope.launch { viewModel.toggleCollapsed(key) } },
        onOpenDetail = onOpenDetail,
        onOpenClaude = viewModel::requestOpenClaude,
        modifier = modifier,
    )
}

@Composable
public fun ProjectsScreen(
    state: ProjectsUiState,
    onSearchChange: (String) -> Unit,
    onToggleFavourite: (String) -> Unit,
    onToggleCollapsed: (String) -> Unit,
    onOpenDetail: (String) -> Unit,
    onOpenClaude: (String) -> Unit,
    modifier: Modifier = Modifier,
) {
    Surface(modifier = modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background) {
        Column(modifier = Modifier.fillMaxSize()) {
            SearchField(value = state.searchText, onValueChange = onSearchChange)
            state.fetchError?.let { InlineNotice(it) }
            state.prefsSyncError?.let { InlineNotice(it) }
            state.openError?.let { InlineNotice(it) }

            val emptyMessage = state.emptyStateMessage
            if (emptyMessage != null) {
                EmptyState(emptyMessage)
            } else {
                ProjectsGrid(
                    state = state,
                    onToggleFavourite = onToggleFavourite,
                    onToggleCollapsed = onToggleCollapsed,
                    onOpenDetail = onOpenDetail,
                    onOpenClaude = onOpenClaude,
                )
            }
        }
    }
}

@Composable
private fun ProjectsGrid(
    state: ProjectsUiState,
    onToggleFavourite: (String) -> Unit,
    onToggleCollapsed: (String) -> Unit,
    onOpenDetail: (String) -> Unit,
    onOpenClaude: (String) -> Unit,
) {
    BoxWithConstraints(modifier = Modifier.fillMaxSize()) {
        val columns = ProjectsGridLayout.columnCount(maxWidth)
        LazyColumn(
            modifier = Modifier.fillMaxSize(),
            contentPadding = androidx.compose.foundation.layout.PaddingValues(Spacing.md12),
            verticalArrangement = Arrangement.spacedBy(Spacing.sm8),
        ) {
            for (group in state.groups) {
                if (group.kind != ProjectGroupKind.FLAT) {
                    item(key = "hdr:${group.key}") {
                        GroupHeader(
                            group = group,
                            isCollapsed = state.isCollapsed(group),
                            onToggle = { onToggleCollapsed(group.key) },
                        )
                    }
                }
                if (!state.isCollapsed(group)) {
                    val rows = group.projects.chunked(columns)
                    rows.forEachIndexed { index, rowProjects ->
                        item(key = "row:${group.key}:$index") {
                            CardRow(
                                rowProjects = rowProjects,
                                columns = columns,
                                groupKey = group.key,
                                state = state,
                                onToggleFavourite = onToggleFavourite,
                                onOpenDetail = onOpenDetail,
                                onOpenClaude = onOpenClaude,
                            )
                        }
                    }
                }
            }
        }
    }
}

@Composable
private fun CardRow(
    rowProjects: List<ProjectInfo>,
    columns: Int,
    groupKey: String,
    state: ProjectsUiState,
    onToggleFavourite: (String) -> Unit,
    onOpenDetail: (String) -> Unit,
    onOpenClaude: (String) -> Unit,
) {
    Row(
        modifier = Modifier.fillMaxWidth(),
        horizontalArrangement = Arrangement.spacedBy(Spacing.sm8),
    ) {
        for (project in rowProjects) {
            Box(modifier = Modifier.weight(1f)) {
                ProjectCard(
                    project = project,
                    displayName = ProjectGrouping.displayLabel(project.name, groupKey),
                    isFavourite = state.isFavourite(project.path),
                    onToggleFavourite = { onToggleFavourite(project.path) },
                    onOpenDetail = { onOpenDetail(project.path) },
                    onOpenClaude = { onOpenClaude(project.path) },
                )
            }
        }
        // Pad a short final row so cards keep a uniform column width.
        repeat(columns - rowProjects.size) { Spacer(modifier = Modifier.weight(1f)) }
    }
}

@Composable
private fun ProjectCard(
    project: ProjectInfo,
    displayName: String,
    isFavourite: Boolean,
    onToggleFavourite: () -> Unit,
    onOpenDetail: () -> Unit,
    onOpenClaude: () -> Unit,
) {
    WebTermCard(modifier = Modifier.fillMaxWidth()) {
        Column(verticalArrangement = Arrangement.spacedBy(Spacing.xs4)) {
            Row(
                verticalAlignment = Alignment.CenterVertically,
                horizontalArrangement = Arrangement.spacedBy(Spacing.xs4),
            ) {
                Text(
                    text = "★",
                    color = if (isFavourite) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.onSurfaceVariant,
                    modifier = Modifier.clickable(onClick = onToggleFavourite),
                )
                Text(
                    text = displayName,
                    style = MaterialTheme.typography.titleSmall,
                    fontWeight = FontWeight.SemiBold,
                    color = MaterialTheme.colorScheme.onSurface,
                    maxLines = 1,
                    overflow = TextOverflow.Ellipsis,
                    modifier = Modifier.weight(1f).clickable(onClick = onOpenDetail),
                )
                if (project.dirty == true) {
                    // Dirty badge: uncommitted changes. Inert glyph (§8).
                    Text(text = "●", color = WebTermColors.statusWaiting)
                }
            }
            project.branch?.let { branch ->
                Row(verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(Spacing.xs4)) {
                    Text(
                        text = branch,
                        style = WebTermType.metaMono,
                        color = MaterialTheme.colorScheme.onSurfaceVariant,
                        maxLines = 1,
                        overflow = TextOverflow.Ellipsis,
                        modifier = Modifier.weight(1f, fill = false),
                    )
                    // W3 sync chip: commits ahead/behind upstream (best-effort; only shown when non-zero).
                    val ahead = project.ahead ?: 0
                    val behind = project.behind ?: 0
                    if (ahead > 0) Text(text = "↑$ahead", style = WebTermType.metaMono, color = WebTermColors.statusWorking)
                    if (behind > 0) Text(text = "↓$behind", style = WebTermType.metaMono, color = WebTermColors.statusWaiting)
                }
            }
            val running = project.sessions.count { !it.exited }
            if (running > 0) {
                Text(
                    text = ProjectsCopy.activeCountBadge(running),
                    style = WebTermType.metaMono,
                    color = MaterialTheme.colorScheme.primary,
                )
            }
            TextButton(onClick = onOpenClaude) { Text(ProjectsCopy.OPEN_CLAUDE_LABEL) }
        }
    }
}

@Composable
private fun GroupHeader(
    group: ProjectGroup,
    isCollapsed: Boolean,
    onToggle: () -> Unit,
) {
    val caret = when {
        !group.isCollapsible -> "●"
        isCollapsed -> "▶"
        else -> "▼"
    }
    val base = Modifier.fillMaxWidth().padding(vertical = Spacing.xs4)
    val rowModifier = if (group.isCollapsible) base.clickable(onClick = onToggle) else base
    Row(
        modifier = rowModifier,
        verticalAlignment = Alignment.CenterVertically,
        horizontalArrangement = Arrangement.spacedBy(Spacing.sm8),
    ) {
        Text(text = caret, color = MaterialTheme.colorScheme.onSurfaceVariant)
        Text(
            text = group.label,
            style = MaterialTheme.typography.titleSmall,
            color = MaterialTheme.colorScheme.onBackground,
            maxLines = 1,
            overflow = TextOverflow.Ellipsis,
            modifier = Modifier.weight(1f),
        )
        Text(text = group.projects.size.toString(), style = WebTermType.metaMono, color = MaterialTheme.colorScheme.onSurfaceVariant)
        if (group.kind != ProjectGroupKind.ACTIVE && group.activeCount > 0) {
            Text(text = ProjectsCopy.activeCountBadge(group.activeCount), style = WebTermType.metaMono, color = MaterialTheme.colorScheme.primary)
        }
    }
}

@Composable
private fun SearchField(value: String, onValueChange: (String) -> Unit) {
    OutlinedTextField(
        value = value,
        onValueChange = onValueChange,
        singleLine = true,
        placeholder = { Text(ProjectsCopy.SEARCH_PROMPT) },
        modifier = Modifier
            .fillMaxWidth()
            .padding(horizontal = Spacing.md12, vertical = Spacing.sm8),
    )
}

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

@Composable
private fun EmptyState(message: String) {
    Column(
        modifier = Modifier
            .fillMaxSize()
            .verticalScroll(rememberScrollState())
            .padding(Spacing.xxl24),
        horizontalAlignment = Alignment.CenterHorizontally,
        verticalArrangement = Arrangement.spacedBy(Spacing.md12, Alignment.CenterVertically),
    ) {
        Text(text = message, style = MaterialTheme.typography.bodyMedium, color = MaterialTheme.colorScheme.onSurfaceVariant)
    }
}

/**
 * The pure regular-width → column-count seam (mirrors iOS `ProjectsGridLayout`). A23 supplies the
 * regular grid; **A26 owns the full adaptive layout** (ListDetail/NavigationSuite + iPad detents), so
 * this stays a single pure decision point, JVM-testable, with no view-tree conditionals.
 */
public object ProjectsGridLayout {
    /** Rise to two columns at this available width (a phone stays single-column). */
    public val twoColumnMinWidth: Dp = 560.dp

    /** Rise to three columns on a wide tablet / desktop-class panel. */
    public val threeColumnMinWidth: Dp = 840.dp

    /** Always `>= 1` (never 0 → never an empty grid). */
    public fun columnCount(availableWidth: Dp): Int = when {
        availableWidth >= threeColumnMinWidth -> 3
        availableWidth >= twoColumnMinWidth -> 2
        else -> 1
    }
}

@Preview(name = "ProjectsScreen — empty")
@Composable
private fun ProjectsScreenPreview() {
    WebTermTheme {
        ProjectsScreen(
            state = ProjectsUiState(hasLoadedOnce = true),
            onSearchChange = {},
            onToggleFavourite = {},
            onToggleCollapsed = {},
            onOpenDetail = {},
            onOpenClaude = {},
        )
    }
}
