/*
 * SPDX-License-Identifier: GPL-3.0-or-later
 */
package com.runicgateway.app.ui.player

import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
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.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.Button
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.input.PasswordVisualTransformation
import androidx.compose.ui.unit.dp
import androidx.compose.foundation.text.KeyboardOptions
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.runicgateway.app.R
import com.runicgateway.app.data.api.dto.RosterCharDto
import com.runicgateway.app.ui.ErrorKind
import com.runicgateway.app.ui.UiState
import com.runicgateway.app.ui.components.ErrorView
import com.runicgateway.app.ui.components.LoadingView
import com.runicgateway.app.ui.components.PillTone
import com.runicgateway.app.ui.components.ShardCard
import com.runicgateway.app.ui.components.StatusPill

/**
 * The player's linked game accounts + character rosters (PLAN.md §6.3). Tapping a
 * character opens its text-only sheet. Not-yet-linked players get the `[link` code
 * prompt (and, when the shard offers it, a hybrid signup form).
 */
@Composable
fun CharactersScreen(
    onOpenChar: (String) -> Unit,
    modifier: Modifier = Modifier,
    viewModel: CharactersViewModel = hiltViewModel(),
) {
    val state by viewModel.state.collectAsStateWithLifecycle()

    when (val accounts = state.accounts) {
        is UiState.Loading -> LoadingView(modifier)
        is UiState.Error -> ErrorView(accounts.kind, onRetry = viewModel::load, modifier = modifier)
        is UiState.Success -> Column(
            modifier = modifier
                .fillMaxSize()
                .verticalScroll(rememberScrollState())
                .padding(16.dp),
            verticalArrangement = Arrangement.spacedBy(12.dp),
        ) {
            LinkCard(state, viewModel)
            if (state.signupEnabled) CreateAccountCard(state, viewModel)

            if (accounts.data.isEmpty()) {
                Text(
                    stringResource(R.string.player_characters_empty),
                    style = MaterialTheme.typography.bodyMedium,
                    color = MaterialTheme.colorScheme.onSurfaceVariant,
                )
            } else {
                accounts.data.forEach { account ->
                    AccountRoster(
                        account = account,
                        roster = state.rosters[account] ?: UiState.Loading,
                        onRetry = { viewModel.loadRoster(account) },
                        onOpenChar = onOpenChar,
                    )
                }
            }
        }
    }
}

@Composable
private fun LinkCard(state: CharactersViewModel.State, viewModel: CharactersViewModel) {
    var code by rememberSaveable { mutableStateOf("") }
    ShardCard(Modifier.fillMaxWidth()) {
        Column(Modifier.padding(16.dp)) {
            Text(stringResource(R.string.player_link_title), style = MaterialTheme.typography.titleMedium)
            Text(
                stringResource(R.string.player_link_hint),
                style = MaterialTheme.typography.bodySmall,
                color = MaterialTheme.colorScheme.onSurfaceVariant,
                modifier = Modifier.padding(top = 6.dp),
            )
            OutlinedTextField(
                value = code,
                onValueChange = { code = it.uppercase() },
                singleLine = true,
                enabled = !state.busy,
                label = { Text(stringResource(R.string.player_link_code)) },
                modifier = Modifier.fillMaxWidth().padding(top = 12.dp),
            )
            Button(
                onClick = { viewModel.link(code); code = "" },
                enabled = !state.busy && code.isNotBlank(),
                modifier = Modifier.padding(top = 12.dp),
            ) { Text(stringResource(R.string.player_link_action)) }
            FormFeedback(state.feedback)
        }
    }
}

@Composable
private fun CreateAccountCard(state: CharactersViewModel.State, viewModel: CharactersViewModel) {
    var account by rememberSaveable { mutableStateOf("") }
    var password by rememberSaveable { mutableStateOf("") }
    ShardCard(Modifier.fillMaxWidth()) {
        Column(Modifier.padding(16.dp)) {
            Text(stringResource(R.string.player_create_title), style = MaterialTheme.typography.titleMedium)
            OutlinedTextField(
                value = account,
                onValueChange = { account = it },
                singleLine = true,
                enabled = !state.busy,
                label = { Text(stringResource(R.string.player_create_account)) },
                modifier = Modifier.fillMaxWidth().padding(top = 12.dp),
            )
            OutlinedTextField(
                value = password,
                onValueChange = { password = it },
                singleLine = true,
                enabled = !state.busy,
                label = { Text(stringResource(R.string.player_create_password)) },
                visualTransformation = PasswordVisualTransformation(),
                keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password),
                modifier = Modifier.fillMaxWidth().padding(top = 12.dp),
            )
            Button(
                onClick = { viewModel.createAccount(account, password); password = "" },
                enabled = !state.busy && account.isNotBlank() && password.length >= 8,
                modifier = Modifier.padding(top = 12.dp),
            ) { Text(stringResource(R.string.player_create_action)) }
        }
    }
}

@Composable
private fun AccountRoster(
    account: String,
    roster: UiState<List<RosterCharDto>>,
    onRetry: () -> Unit,
    onOpenChar: (String) -> Unit,
) {
    Column {
        Text(
            account,
            style = MaterialTheme.typography.titleSmall,
            color = MaterialTheme.colorScheme.primary,
            modifier = Modifier.padding(vertical = 8.dp),
        )
        when (roster) {
            is UiState.Loading -> Text(
                stringResource(R.string.player_roster_loading),
                style = MaterialTheme.typography.bodySmall,
                color = MaterialTheme.colorScheme.onSurfaceVariant,
            )
            is UiState.Error -> RosterError(roster.kind, onRetry)
            is UiState.Success -> {
                if (roster.data.isEmpty()) {
                    Text(
                        stringResource(R.string.player_roster_empty),
                        style = MaterialTheme.typography.bodySmall,
                        color = MaterialTheme.colorScheme.onSurfaceVariant,
                    )
                } else {
                    roster.data.forEach { CharRow(it, onOpenChar) }
                }
            }
        }
    }
}

@Composable
private fun RosterError(kind: ErrorKind, onRetry: () -> Unit) {
    Row(verticalAlignment = Alignment.CenterVertically) {
        Text(
            stringResource(
                if (kind == ErrorKind.SHARD_OFFLINE) R.string.error_shard_offline else R.string.error_server,
            ),
            style = MaterialTheme.typography.bodySmall,
            color = MaterialTheme.colorScheme.onSurfaceVariant,
            modifier = Modifier.weight(1f),
        )
        TextButton(onClick = onRetry) { Text(stringResource(R.string.action_retry)) }
    }
}

@Composable
private fun CharRow(char: RosterCharDto, onOpenChar: (String) -> Unit) {
    ShardCard(
        Modifier
            .fillMaxWidth()
            .padding(vertical = 4.dp)
            .clickable(enabled = char.serial.isNotBlank()) { onOpenChar(char.serial) },
    ) {
        Row(
            Modifier.fillMaxWidth().padding(14.dp),
            verticalAlignment = Alignment.CenterVertically,
        ) {
            Text(
                char.name ?: stringResource(R.string.player_char_unknown),
                style = MaterialTheme.typography.bodyLarge,
                modifier = Modifier.weight(1f),
            )
            StatusPill(
                text = stringResource(
                    if (char.online) R.string.player_char_online else R.string.player_char_offline,
                ),
                tone = if (char.online) PillTone.Success else PillTone.Neutral,
                modifier = Modifier.padding(end = 8.dp),
            )
            Text("›", style = MaterialTheme.typography.titleMedium, color = MaterialTheme.colorScheme.onSurfaceVariant)
        }
    }
}

@Composable
private fun FormFeedback(feedback: CharactersViewModel.Feedback?) {
    if (feedback == null) return
    Text(
        text = stringResource(feedback.messageRes),
        style = MaterialTheme.typography.bodySmall,
        color = if (feedback.ok) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.error,
        modifier = Modifier.padding(top = 10.dp),
    )
}
