package club.clubk.ktag.apps.terminal

import android.content.Intent
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.background
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.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.imePadding
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.Send
import androidx.compose.material.icons.filled.Clear
import androidx.compose.material.icons.filled.Refresh
import androidx.compose.material.icons.filled.Share
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.DropdownMenu
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
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.platform.LocalContext
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.font.Font
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.style.TextDecoration
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import club.clubk.ktag.apps.core.UsbSerialManager
import club.clubk.ktag.apps.terminal.ui.theme.KTagDarkGray
import club.clubk.ktag.apps.terminal.ui.theme.KTagYellow
import club.clubk.ktag.apps.terminal.ui.theme.TerminalTheme
import com.hoho.android.usbserial.driver.UsbSerialDriver
import kotlinx.coroutines.launch

private val TerminalFontFamily = FontFamily(
    Font(R.font.jetbrains_mono_regular, FontWeight.Normal),
    Font(R.font.jetbrains_mono_bold, FontWeight.Bold)
)

class TerminalActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            TerminalTheme {
                Surface(
                    modifier = Modifier.fillMaxSize(),
                    color = MaterialTheme.colorScheme.background
                ) {
                    TerminalScreen()
                }
            }
        }
    }
}

data class TerminalLine(
    val timestamp: Long,
    val direction: Direction,
    val content: String
) {
    enum class Direction { RX, TX, SYSTEM }
}

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun TerminalScreen() {
    val context = LocalContext.current
    val scope = rememberCoroutineScope()
    val listState = rememberLazyListState()

    var connectionState by remember { mutableStateOf(UsbSerialManager.ConnectionState.DISCONNECTED) }
    var inputText by remember { mutableStateOf("") }
    var errorMessage by remember { mutableStateOf<String?>(null) }
    var availableDevices by remember { mutableStateOf<List<UsbSerialDriver>>(emptyList()) }
    var selectedDevice by remember { mutableStateOf<UsbSerialDriver?>(null) }
    var showDeviceMenu by remember { mutableStateOf(false) }
    val terminalLines = remember { mutableStateListOf<TerminalLine>() }
    val mainHandler = remember { Handler(Looper.getMainLooper()) }
    val rxBuffer = remember { StringBuilder() }

    val usbSerialManager = remember {
        UsbSerialManager(context, object : UsbSerialManager.Listener {
            override fun onDataReceived(data: ByteArray) {
                val text = String(data, Charsets.UTF_8)
                mainHandler.post {
                    rxBuffer.append(text)
                    // Split on newlines and emit complete lines
                    var newlineIndex: Int
                    while (rxBuffer.indexOf('\n').also { newlineIndex = it } != -1) {
                        var lineEnd = newlineIndex
                        // Handle \r\n by stripping the \r as well
                        if (lineEnd > 0 && rxBuffer[lineEnd - 1] == '\r') {
                            lineEnd--
                        }
                        val lineContent = rxBuffer.substring(0, lineEnd)
                        rxBuffer.delete(0, newlineIndex + 1)
                        terminalLines.add(TerminalLine(System.currentTimeMillis(), TerminalLine.Direction.RX, lineContent))
                    }
                    // Limit buffer size
                    while (terminalLines.size > MAX_LINES) {
                        terminalLines.removeAt(0)
                    }
                }
            }

            override fun onConnectionStateChanged(state: UsbSerialManager.ConnectionState) {
                mainHandler.post {
                    connectionState = state
                    if (state == UsbSerialManager.ConnectionState.CONNECTED) {
                        rxBuffer.clear()
                        terminalLines.add(
                            TerminalLine(
                                System.currentTimeMillis(),
                                TerminalLine.Direction.SYSTEM,
                                "Connected to ${selectedDevice?.device?.deviceName ?: "device"}"
                            )
                        )
                    } else if (state == UsbSerialManager.ConnectionState.DISCONNECTED) {
                        // Flush any remaining buffered data
                        if (rxBuffer.isNotEmpty()) {
                            terminalLines.add(TerminalLine(System.currentTimeMillis(), TerminalLine.Direction.RX, rxBuffer.toString()))
                            rxBuffer.clear()
                        }
                        terminalLines.add(
                            TerminalLine(
                                System.currentTimeMillis(),
                                TerminalLine.Direction.SYSTEM,
                                "Disconnected"
                            )
                        )
                    }
                }
            }

            override fun onError(message: String) {
                mainHandler.post {
                    errorMessage = message
                    terminalLines.add(
                        TerminalLine(
                            System.currentTimeMillis(),
                            TerminalLine.Direction.SYSTEM,
                            "Error: $message"
                        )
                    )
                }
            }

            override fun onDevicesChanged(devices: List<UsbSerialDriver>) {
                mainHandler.post {
                    availableDevices = devices
                    if (selectedDevice != null && devices.none { it.device.deviceId == selectedDevice?.device?.deviceId }) {
                        selectedDevice = null
                    }
                }
            }

            override fun onAutoConnecting(driver: UsbSerialDriver) {
                mainHandler.post {
                    selectedDevice = driver
                }
            }
        }, actionUsbPermission = "club.clubk.ktag.apps.terminal.USB_PERMISSION")
    }

    DisposableEffect(usbSerialManager) {
        usbSerialManager.register()
        onDispose {
            usbSerialManager.unregister()
        }
    }

    // Auto-scroll when new lines are added
    LaunchedEffect(terminalLines.size) {
        if (terminalLines.isNotEmpty()) {
            listState.animateScrollToItem(terminalLines.size - 1)
        }
    }

    fun sendCommand() {
        if (inputText.isNotBlank() && connectionState == UsbSerialManager.ConnectionState.CONNECTED) {
            val command = inputText
            terminalLines.add(TerminalLine(System.currentTimeMillis(), TerminalLine.Direction.TX, command))
            usbSerialManager.write((command + "\r\n").toByteArray())
            inputText = ""
            // Limit buffer size
            while (terminalLines.size > MAX_LINES) {
                terminalLines.removeAt(0)
            }
        }
    }

    Scaffold(
        topBar = {
            TopAppBar(
                title = { Text("KTag Terminal") },
                colors = TopAppBarDefaults.topAppBarColors(
                    containerColor = KTagDarkGray,
                    titleContentColor = KTagYellow,
                    actionIconContentColor = KTagYellow
                ),
                actions = {
                    IconButton(onClick = { usbSerialManager.refreshDevices() }) {
                        Icon(Icons.Default.Refresh, contentDescription = "Refresh devices")
                    }
                    IconButton(
                        onClick = {
                            val text = terminalLines.joinToString("\n") { line ->
                                val prefix = when (line.direction) {
                                    TerminalLine.Direction.RX -> ""
                                    TerminalLine.Direction.TX -> ">> "
                                    TerminalLine.Direction.SYSTEM -> "-- "
                                }
                                prefix + stripAnsi(line.content)
                            }
                            val sendIntent = Intent().apply {
                                action = Intent.ACTION_SEND
                                putExtra(Intent.EXTRA_TEXT, text)
                                type = "text/plain"
                            }
                            val shareIntent = Intent.createChooser(sendIntent, null)
                            context.startActivity(shareIntent)
                        },
                        enabled = terminalLines.isNotEmpty()
                    ) {
                        Icon(Icons.Default.Share, contentDescription = "Share terminal history")
                    }
                    IconButton(onClick = { terminalLines.clear() }) {
                        Icon(Icons.Default.Clear, contentDescription = "Clear terminal")
                    }
                }
            )
        }
    ) { paddingValues ->
        Column(
            modifier = Modifier
                .fillMaxSize()
                .padding(paddingValues)
                .imePadding()
        ) {
            // Connection controls
            Row(
                modifier = Modifier
                    .fillMaxWidth()
                    .padding(8.dp),
                horizontalArrangement = Arrangement.spacedBy(8.dp),
                verticalAlignment = Alignment.CenterVertically
            ) {
                Box(modifier = Modifier.weight(1f)) {
                    OutlinedButton(
                        onClick = { showDeviceMenu = true },
                        modifier = Modifier.fillMaxWidth()
                    ) {
                        Text(
                            text = selectedDevice?.device?.deviceName
                                ?: if (availableDevices.isEmpty()) "No devices" else "Select device",
                            maxLines = 1
                        )
                    }
                    DropdownMenu(
                        expanded = showDeviceMenu,
                        onDismissRequest = { showDeviceMenu = false }
                    ) {
                        if (availableDevices.isEmpty()) {
                            DropdownMenuItem(
                                text = { Text("No USB serial devices found") },
                                onClick = { showDeviceMenu = false }
                            )
                        } else {
                            availableDevices.forEach { driver ->
                                DropdownMenuItem(
                                    text = {
                                        Text("${driver.device.deviceName} (${driver.javaClass.simpleName.removeSuffix("Driver")})")
                                    },
                                    onClick = {
                                        selectedDevice = driver
                                        showDeviceMenu = false
                                    }
                                )
                            }
                        }
                    }
                }

                Button(
                    onClick = {
                        when (connectionState) {
                            UsbSerialManager.ConnectionState.CONNECTED -> {
                                usbSerialManager.disconnect()
                            }
                            UsbSerialManager.ConnectionState.DISCONNECTED,
                            UsbSerialManager.ConnectionState.ERROR -> {
                                selectedDevice?.let { usbSerialManager.connect(it) }
                            }
                            else -> { /* Connecting/awaiting permission - do nothing */ }
                        }
                    },
                    enabled = selectedDevice != null &&
                            connectionState != UsbSerialManager.ConnectionState.CONNECTING &&
                            connectionState != UsbSerialManager.ConnectionState.AWAITING_PERMISSION,
                    colors = ButtonDefaults.buttonColors(
                        containerColor = if (connectionState == UsbSerialManager.ConnectionState.CONNECTED)
                            MaterialTheme.colorScheme.error
                        else
                            MaterialTheme.colorScheme.primary
                    )
                ) {
                    Text(
                        when (connectionState) {
                            UsbSerialManager.ConnectionState.CONNECTED -> "Disconnect"
                            UsbSerialManager.ConnectionState.CONNECTING -> "Connecting..."
                            UsbSerialManager.ConnectionState.AWAITING_PERMISSION -> "Requesting..."
                            else -> "Connect"
                        }
                    )
                }
            }

            // Terminal display
            LazyColumn(
                state = listState,
                modifier = Modifier
                    .weight(1f)
                    .fillMaxWidth()
                    .background(Color(0xFF323031))
                    .padding(8.dp)
            ) {
                items(terminalLines) { line ->
                    TerminalLineRow(line)
                }
            }

            // Input area
            Row(
                modifier = Modifier
                    .fillMaxWidth()
                    .padding(8.dp),
                horizontalArrangement = Arrangement.spacedBy(8.dp),
                verticalAlignment = Alignment.CenterVertically
            ) {
                OutlinedTextField(
                    value = inputText,
                    onValueChange = { inputText = it },
                    modifier = Modifier.weight(1f),
                    placeholder = { Text("Enter command...", fontFamily = TerminalFontFamily) },
                    singleLine = true,
                    enabled = connectionState == UsbSerialManager.ConnectionState.CONNECTED,
                    keyboardOptions = KeyboardOptions(imeAction = ImeAction.Send),
                    keyboardActions = KeyboardActions(onSend = { sendCommand() }),
                    textStyle = androidx.compose.ui.text.TextStyle(fontFamily = TerminalFontFamily)
                )

                IconButton(
                    onClick = { sendCommand() },
                    enabled = connectionState == UsbSerialManager.ConnectionState.CONNECTED && inputText.isNotBlank()
                ) {
                    Icon(Icons.AutoMirrored.Filled.Send, contentDescription = "Send")
                }
            }
        }
    }
}

@Composable
fun TerminalLineRow(line: TerminalLine) {
    val (prefix, defaultColor) = when (line.direction) {
        TerminalLine.Direction.RX -> "" to Color(0xFFCCCCCC)      // Light gray for RX
        TerminalLine.Direction.TX -> ">> " to Color(0xFF4D6CFA)   // KTag Blue for TX
        TerminalLine.Direction.SYSTEM -> "-- " to Color(0xFF808080)  // Gray for system
    }

    val styledText = if (line.direction == TerminalLine.Direction.RX) {
        parseAnsi(line.content, defaultColor)
    } else {
        buildAnnotatedString {
            pushStyle(SpanStyle(color = defaultColor))
            append(prefix)
            append(line.content)
            pop()
        }
    }

    Text(
        text = styledText,
        style = androidx.compose.ui.text.TextStyle(
            fontFamily = TerminalFontFamily,
            fontSize = 12.sp
        ),
        modifier = Modifier.padding(vertical = 1.dp)
    )
}

private fun parseAnsi(text: String, defaultColor: Color): AnnotatedString {
    return buildAnnotatedString {
        var currentColor = defaultColor
        var currentBgColor: Color? = null
        var isBold = false
        var isItalic = false
        var isUnderline = false
        var isDim = false

        val ansiPattern = Regex("\u001B\\[([0-9;]*)m")
        var lastEnd = 0

        ansiPattern.findAll(text).forEach { match ->
            // Append text before this escape sequence
            if (match.range.first > lastEnd) {
                pushStyle(SpanStyle(
                    color = if (isDim) currentColor.copy(alpha = 0.5f) else currentColor,
                    background = currentBgColor ?: Color.Transparent,
                    fontWeight = if (isBold) FontWeight.Bold else FontWeight.Normal,
                    fontStyle = if (isItalic) FontStyle.Italic else FontStyle.Normal,
                    textDecoration = if (isUnderline) TextDecoration.Underline else TextDecoration.None
                ))
                append(text.substring(lastEnd, match.range.first))
                pop()
            }
            lastEnd = match.range.last + 1

            // Parse the escape codes
            val codes = match.groupValues[1].split(";").mapNotNull { it.toIntOrNull() }
            if (codes.isEmpty()) {
                // Reset
                currentColor = defaultColor
                currentBgColor = null
                isBold = false
                isItalic = false
                isUnderline = false
                isDim = false
            } else {
                var i = 0
                while (i < codes.size) {
                    when (codes[i]) {
                        0 -> {
                            currentColor = defaultColor
                            currentBgColor = null
                            isBold = false
                            isItalic = false
                            isUnderline = false
                            isDim = false
                        }
                        1 -> isBold = true
                        2 -> isDim = true
                        3 -> isItalic = true
                        4 -> isUnderline = true
                        22 -> { isBold = false; isDim = false }
                        23 -> isItalic = false
                        24 -> isUnderline = false
                        // Standard foreground colors (30-37)
                        30 -> currentColor = Color(0xFF000000)  // Black
                        31 -> currentColor = Color(0xFFCD3131)  // Red
                        32 -> currentColor = Color(0xFF0DBC79)  // Green
                        33 -> currentColor = Color(0xFFE5E510)  // Yellow
                        34 -> currentColor = Color(0xFF2472C8)  // Blue
                        35 -> currentColor = Color(0xFFBC3FBC)  // Magenta
                        36 -> currentColor = Color(0xFF11A8CD)  // Cyan
                        37 -> currentColor = Color(0xFFE5E5E5)  // White
                        39 -> currentColor = defaultColor       // Default foreground
                        // Bright foreground colors (90-97)
                        90 -> currentColor = Color(0xFF666666)  // Bright black (gray)
                        91 -> currentColor = Color(0xFFF14C4C)  // Bright red
                        92 -> currentColor = Color(0xFF23D18B)  // Bright green
                        93 -> currentColor = Color(0xFFF5F543)  // Bright yellow
                        94 -> currentColor = Color(0xFF3B8EEA)  // Bright blue
                        95 -> currentColor = Color(0xFFD670D6)  // Bright magenta
                        96 -> currentColor = Color(0xFF29B8DB)  // Bright cyan
                        97 -> currentColor = Color(0xFFFFFFFF)  // Bright white
                        // Standard background colors (40-47)
                        40 -> currentBgColor = Color(0xFF000000)
                        41 -> currentBgColor = Color(0xFFCD3131)
                        42 -> currentBgColor = Color(0xFF0DBC79)
                        43 -> currentBgColor = Color(0xFFE5E510)
                        44 -> currentBgColor = Color(0xFF2472C8)
                        45 -> currentBgColor = Color(0xFFBC3FBC)
                        46 -> currentBgColor = Color(0xFF11A8CD)
                        47 -> currentBgColor = Color(0xFFE5E5E5)
                        49 -> currentBgColor = null             // Default background
                        // 256-color mode (38;5;n or 48;5;n)
                        38 -> {
                            if (i + 2 < codes.size && codes[i + 1] == 5) {
                                currentColor = ansi256ToColor(codes[i + 2], defaultColor)
                                i += 2
                            }
                        }
                        48 -> {
                            if (i + 2 < codes.size && codes[i + 1] == 5) {
                                currentBgColor = ansi256ToColor(codes[i + 2], defaultColor)
                                i += 2
                            }
                        }
                    }
                    i++
                }
            }
        }

        // Append remaining text
        if (lastEnd < text.length) {
            pushStyle(SpanStyle(
                color = if (isDim) currentColor.copy(alpha = 0.5f) else currentColor,
                background = currentBgColor ?: Color.Transparent,
                fontWeight = if (isBold) FontWeight.Bold else FontWeight.Normal,
                fontStyle = if (isItalic) FontStyle.Italic else FontStyle.Normal,
                textDecoration = if (isUnderline) TextDecoration.Underline else TextDecoration.None
            ))
            append(text.substring(lastEnd))
            pop()
        }
    }
}

private fun ansi256ToColor(code: Int, defaultColor: Color): Color {
    return when {
        code < 16 -> {
            // Standard colors
            val standardColors = listOf(
                Color(0xFF000000), Color(0xFFCD3131), Color(0xFF0DBC79), Color(0xFFE5E510),
                Color(0xFF2472C8), Color(0xFFBC3FBC), Color(0xFF11A8CD), Color(0xFFE5E5E5),
                Color(0xFF666666), Color(0xFFF14C4C), Color(0xFF23D18B), Color(0xFFF5F543),
                Color(0xFF3B8EEA), Color(0xFFD670D6), Color(0xFF29B8DB), Color(0xFFFFFFFF)
            )
            standardColors.getOrElse(code) { defaultColor }
        }
        code < 232 -> {
            // 216-color cube (6x6x6)
            val index = code - 16
            val r = (index / 36) * 51
            val g = ((index / 6) % 6) * 51
            val b = (index % 6) * 51
            Color(0xFF000000 or (r shl 16).toLong() or (g shl 8).toLong() or b.toLong())
        }
        else -> {
            // Grayscale (24 shades)
            val gray = (code - 232) * 10 + 8
            Color(0xFF000000 or (gray shl 16).toLong() or (gray shl 8).toLong() or gray.toLong())
        }
    }
}

private fun stripAnsi(text: String): String {
    return text.replace(Regex("\u001B\\[[0-9;]*m"), "")
}

private const val MAX_LINES = 10000
