package solutions.tretter.githugandroid

import androidx.compose.foundation.background
import androidx.compose.foundation.horizontalScroll
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.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.imePadding
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.selection.SelectionContainer
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.TextField
import androidx.compose.material3.TextFieldDefaults
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
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.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.onSizeChanged
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.TextLayoutResult
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties

data class ManPageState(
    val topic: String,
    val content: String,
)

private data class ManPageMatch(
    val start: Int,
    val end: Int,
    val lineIndex: Int,
    val columnIndex: Int,
)

@Composable
fun ManPageDialog(
    state: ManPageState,
    onClose: () -> Unit,
) {
    val horizontalScrollState = rememberScrollState()
    val verticalScrollState = rememberScrollState()
    var contentViewportSize by remember { mutableStateOf(IntSize.Zero) }
    var textLayoutResult by remember { mutableStateOf<TextLayoutResult?>(null) }
    var searchQuery by remember(state.content) { mutableStateOf("") }
    var selectedMatch by remember(state.content) { mutableStateOf(0) }
    val contentLines = remember(state.content) { state.content.lines() }
    val matches = remember(searchQuery, contentLines) {
        if (searchQuery.isBlank()) {
            emptyList()
        } else {
            val result = mutableListOf<ManPageMatch>()
            var absoluteLineStart = 0
            contentLines.forEachIndexed { lineIndex, line ->
                var searchFrom = 0
                while (searchFrom <= line.length) {
                    val columnIndex = line.indexOf(searchQuery, searchFrom, ignoreCase = true)
                    if (columnIndex == -1) break
                    val start = absoluteLineStart + columnIndex
                    result += ManPageMatch(
                        start = start,
                        end = start + searchQuery.length,
                        lineIndex = lineIndex,
                        columnIndex = columnIndex,
                    )
                    searchFrom = columnIndex + searchQuery.length.coerceAtLeast(1)
                }
                absoluteLineStart += line.length + 1
            }
            result
        }
    }
    val matchCount = matches.size
    val currentMatchNumber = if (matchCount == 0) 0 else selectedMatch + 1
    val highlightedContent = remember(state.content, matches, selectedMatch) {
        AnnotatedString.Builder(state.content).apply {
            matches.forEachIndexed { index, match ->
                addStyle(
                    SpanStyle(
                        color = if (index == selectedMatch) Color.White else Color.Black,
                        background = if (index == selectedMatch) Color(0xFFD32F2F) else Color(0xFFFFEB3B),
                    ),
                    match.start,
                    match.end,
                )
            }
        }.toAnnotatedString()
    }

    LaunchedEffect(searchQuery, matchCount) {
        selectedMatch = selectedMatch.coerceIn(0, (matchCount - 1).coerceAtLeast(0))
    }

    LaunchedEffect(selectedMatch, matches, textLayoutResult, contentViewportSize) {
        val match = matches.getOrNull(selectedMatch) ?: return@LaunchedEffect
        val layoutResult = textLayoutResult ?: return@LaunchedEffect
        val startBounds = layoutResult.getBoundingBox(match.start)
        val endBounds = layoutResult.getBoundingBox((match.end - 1).coerceAtLeast(match.start))
        val viewportHeight = contentViewportSize.height.takeIf { it > 0 } ?: 0
        val viewportWidth = contentViewportSize.width.takeIf { it > 0 } ?: 0
        val highlightCenterY = (startBounds.top + endBounds.bottom) / 2f
        val highlightCenterX = (startBounds.left + endBounds.right) / 2f
        val verticalTarget = (
            highlightCenterY - viewportHeight / 2f
            ).toInt()
            .coerceIn(0, verticalScrollState.maxValue)
        val horizontalTarget = (
            highlightCenterX - viewportWidth / 2f
            ).toInt()
            .coerceIn(0, horizontalScrollState.maxValue)
        verticalScrollState.animateScrollTo(verticalTarget)
        horizontalScrollState.animateScrollTo(horizontalTarget)
    }

    Dialog(
        onDismissRequest = onClose,
        properties = DialogProperties(usePlatformDefaultWidth = false),
    ) {
        Surface(
            modifier = Modifier
                .fillMaxWidth()
                .fillMaxHeight(0.86f)
                .imePadding(),
            color = PanelPrimary,
            shape = RoundedCornerShape(8.dp),
        ) {
            Column(
                modifier = Modifier.padding(12.dp),
                verticalArrangement = Arrangement.spacedBy(10.dp),
            ) {
                Text(
                    text = "Git Manpage",
                    color = TextPrimary,
                    fontWeight = FontWeight.Bold,
                    fontSize = 20.sp,
                )
                Row(
                    modifier = Modifier.fillMaxWidth(),
                    horizontalArrangement = Arrangement.spacedBy(8.dp),
                ) {
                    Button(
                        onClick = onClose,
                        colors = ButtonDefaults.buttonColors(
                            containerColor = Accent,
                            contentColor = AppBackground,
                        ),
                    ) {
                        Text("Close")
                    }
                }
                Row(
                    modifier = Modifier.fillMaxWidth(),
                    horizontalArrangement = Arrangement.spacedBy(8.dp),
                ) {
                    TextField(
                        value = searchQuery,
                        onValueChange = {
                            searchQuery = it
                            selectedMatch = 0
                        },
                        modifier = Modifier.weight(1f),
                        singleLine = true,
                        placeholder = { Text("Search") },
                        colors = TextFieldDefaults.colors(
                            focusedTextColor = TextPrimary,
                            unfocusedTextColor = TextPrimary,
                            focusedContainerColor = PanelSecondary,
                            unfocusedContainerColor = PanelSecondary,
                            focusedIndicatorColor = Accent,
                            unfocusedIndicatorColor = TextMuted,
                            cursorColor = Accent,
                        ),
                    )
                    Button(
                        onClick = {
                            if (matchCount > 0) selectedMatch = (selectedMatch - 1 + matchCount) % matchCount
                        },
                        enabled = matchCount > 0,
                        colors = ButtonDefaults.buttonColors(
                            containerColor = PanelSecondary,
                            contentColor = TextPrimary,
                        ),
                    ) {
                        Text("Prev")
                    }
                    Button(
                        onClick = {
                            if (matchCount > 0) selectedMatch = (selectedMatch + 1) % matchCount
                        },
                        enabled = matchCount > 0,
                        colors = ButtonDefaults.buttonColors(
                            containerColor = PanelSecondary,
                            contentColor = TextPrimary,
                        ),
                    ) {
                        Text("Next")
                    }
                }
                Text(
                    text = "$currentMatchNumber/$matchCount",
                    color = TextSecondary,
                    fontSize = 12.sp,
                )
                Text(
                    text = "git ${state.topic}",
                    color = TextPrimary,
                    fontWeight = FontWeight.Bold,
                )
                Box(
                    modifier = Modifier
                        .fillMaxWidth()
                        .weight(1f)
                        .onSizeChanged { contentViewportSize = it }
                        .background(TerminalBackground, RoundedCornerShape(6.dp))
                        .padding(10.dp)
                        .horizontalScroll(horizontalScrollState)
                        .verticalScroll(verticalScrollState),
                ) {
                    SelectionContainer {
                        Text(
                            text = highlightedContent,
                            modifier = Modifier.widthIn(min = 1200.dp),
                            onTextLayout = { textLayoutResult = it },
                            color = TextPrimary,
                            fontFamily = FontFamily.Monospace,
                            fontSize = 13.sp,
                            softWrap = false,
                        )
                    }
                }
            }
        }
    }
}
