package com.anypreta.bountyboard.ui.admin

import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Switch
import androidx.compose.material3.SwitchDefaults
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateMapOf
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.text.input.KeyboardType
import androidx.compose.ui.unit.dp
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import androidx.lifecycle.viewmodel.compose.viewModel
import com.anypreta.bountyboard.data.AppContainer
import com.anypreta.bountyboard.di.LocalAppContainer
import com.anypreta.bountyboard.di.containerVMFactory
import com.anypreta.bountyboard.ui.components.BBButton
import com.anypreta.bountyboard.ui.components.BBButtonStyle
import com.anypreta.bountyboard.ui.components.BBCard
import com.anypreta.bountyboard.ui.components.BBTextField
import com.anypreta.bountyboard.ui.components.Hairline
import com.anypreta.bountyboard.ui.components.MessageBox
import com.anypreta.bountyboard.ui.components.MessageKind
import com.anypreta.bountyboard.ui.components.ScreenColumn
import com.anypreta.bountyboard.ui.components.ScreenState
import com.anypreta.bountyboard.ui.components.SectionLabel
import com.anypreta.bountyboard.ui.theme.BB
import com.google.gson.JsonObject
import kotlinx.coroutines.launch

class AdminSettingsViewModel(private val container: AppContainer) : ViewModel() {

    var loading by mutableStateOf(true)
        private set
    var error by mutableStateOf<String?>(null)
        private set
    var settings by mutableStateOf<JsonObject?>(null)
        private set
    var saving by mutableStateOf(false)
        private set
    var actionError by mutableStateOf<String?>(null)
        private set
    var savedOk by mutableStateOf(false)
        private set

    init {
        load()
    }

    fun load() {
        loading = true; error = null
        viewModelScope.launch {
            try {
                settings = container.repo.call { adminSettings() }
            } catch (e: Exception) {
                error = e.message
            } finally {
                loading = false
            }
        }
    }

    fun save(edited: Map<String, Any>) {
        saving = true; actionError = null; savedOk = false
        viewModelScope.launch {
            try {
                val body = container.gson.toJsonTree(edited).asJsonObject
                settings = container.repo.call { adminPatchSettings(body) }
                savedOk = true
            } catch (e: Exception) {
                actionError = e.message
            } finally {
                saving = false
            }
        }
    }
}

private fun humanize(key: String): String =
    key.replace(Regex("([a-z])([A-Z])"), "$1 $2")
        .replace('_', ' ')
        .replaceFirstChar { it.uppercase() }

@Composable
fun AdminSettingsScreen() {
    val container = LocalAppContainer.current
    val vm: AdminSettingsViewModel = viewModel(factory = containerVMFactory(container) { AdminSettingsViewModel(it) })

    ScreenState(loading = vm.loading, error = vm.error, onRetry = { vm.load() }) {
        val settings = vm.settings ?: return@ScreenState
        SettingsForm(settings = settings, vm = vm)
    }
}

@Composable
private fun SettingsForm(settings: JsonObject, vm: AdminSettingsViewModel) {
    val p = BB.colors
    // edited holds the current value for each editable flat key.
    val edited = remember(settings) {
        mutableStateMapOf<String, Any>().apply {
            settings.entrySet().forEach { (k, v) ->
                if (v.isJsonPrimitive) {
                    val prim = v.asJsonPrimitive
                    when {
                        prim.isBoolean -> put(k, prim.asBoolean)
                        prim.isNumber -> put(k, prim.asString)
                        else -> put(k, prim.asString)
                    }
                }
            }
        }
    }

    ScreenColumn {
        Text("System settings", style = MaterialTheme.typography.headlineMedium, color = p.text)

        // Top-level primitives.
        BBCard(modifier = Modifier.fillMaxWidth()) {
            Column(verticalArrangement = Arrangement.spacedBy(12.dp)) {
                val flat = settings.entrySet().filter { it.value.isJsonPrimitive }
                if (flat.isEmpty()) {
                    Text("No editable top-level settings.", style = MaterialTheme.typography.bodyMedium, color = p.muted)
                }
                flat.forEachIndexed { i, (key, value) ->
                    if (i > 0) Hairline()
                    val prim = value.asJsonPrimitive
                    if (prim.isBoolean) {
                        Row(Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.SpaceBetween) {
                            Text(humanize(key), style = MaterialTheme.typography.bodyLarge, color = p.text, modifier = Modifier.weight(1f))
                            Switch(
                                checked = edited[key] as? Boolean ?: false,
                                onCheckedChange = { edited[key] = it },
                                colors = SwitchDefaults.colors(
                                    checkedThumbColor = p.onAccent,
                                    checkedTrackColor = p.accent,
                                    uncheckedTrackColor = p.surface2,
                                    uncheckedBorderColor = p.border,
                                ),
                            )
                        }
                    } else {
                        BBTextField(
                            value = edited[key] as? String ?: "",
                            onValueChange = { edited[key] = it },
                            label = humanize(key),
                            keyboardType = if (prim.isNumber) KeyboardType.Number else KeyboardType.Text,
                            modifier = Modifier.fillMaxWidth(),
                        )
                    }
                }
            }
        }

        // Nested objects — show one level of primitive children (read-only display).
        settings.entrySet().filter { it.value.isJsonObject }.forEach { (key, value) ->
            val obj = value.asJsonObject
            BBCard(modifier = Modifier.fillMaxWidth()) {
                Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
                    SectionLabel(humanize(key))
                    obj.entrySet().filter { it.value.isJsonPrimitive }.forEach { (ck, cv) ->
                        Row(Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) {
                            Text(humanize(ck), style = MaterialTheme.typography.bodyMedium, color = p.muted)
                            Text(cv.asString, style = MaterialTheme.typography.bodyMedium, color = p.text)
                        }
                    }
                }
            }
        }

        vm.actionError?.let { MessageBox(it, MessageKind.Error) }
        if (vm.savedOk) MessageBox("Settings saved.", MessageKind.Ok)

        BBButton(
            "Save",
            onClick = {
                // Convert numeric strings back to numbers where the original was a number.
                val out = edited.mapValues { (k, v) ->
                    val orig = settings.get(k)
                    if (v is String && orig != null && orig.isJsonPrimitive && orig.asJsonPrimitive.isNumber) {
                        v.toDoubleOrNull() ?: v
                    } else {
                        v
                    }
                }
                vm.save(out)
            },
            style = BBButtonStyle.Primary,
            loading = vm.saving,
            small = true,
        )
    }
}
