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

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.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.runicgateway.app.R
import com.runicgateway.app.data.api.dto.OnlineStaffDto
import com.runicgateway.app.data.api.dto.ShardStatusDto
import com.runicgateway.app.ui.UiState
import com.runicgateway.app.ui.components.ErrorView
import com.runicgateway.app.ui.components.FeatureCard
import com.runicgateway.app.ui.components.LoadingView
import com.runicgateway.app.data.repository.ShardFeature
import com.runicgateway.app.data.repository.ShardFeatures
import com.runicgateway.app.data.repository.canSee
import com.runicgateway.app.ui.components.PillTone
import com.runicgateway.app.ui.components.SectionLabel
import com.runicgateway.app.ui.components.ShardCard
import com.runicgateway.app.ui.components.StatusPill

/**
 * Board destinations reachable from the hub, each tagged with the visibility feature
 * that governs it (M11). An admin can switch any of these off or raise its audience,
 * so the hub's board list is filtered the same way the drawer is — a tile whose
 * feature the caller can't see would only lead to a `404`/`403`.
 */
enum class ShardBoard(val feature: String) {
    CHAMPS(ShardFeature.CHAMPS),
    GUILDS(ShardFeature.GUILDS),
    GOVERNORS(ShardFeature.GOVERNORS),
    HOUSES(ShardFeature.HOUSES),
}

/**
 * The boards this viewer may reach. Pure + side-effect-free so the gating is
 * unit-tested without Compose, exactly like `visibleEntries` for the drawer. An
 * unknown answer shows every board — the server gates regardless (see [canSee]).
 */
fun visibleBoards(features: ShardFeatures?): List<ShardBoard> =
    ShardBoard.entries.filter { canSee(features, it.feature) }

/**
 * The Shard hub (PLAN.md §6.2): live connection status, online count + latest
 * economy, presence, staff online, links to the boards, and a live activity feed.
 * The whole screen degrades gracefully — status drives loading/error, the feed
 * simply shows "offline" when the SSE stream is down (§7).
 */
@Composable
fun ShardScreen(
    onOpenBoard: (ShardBoard) -> Unit,
    modifier: Modifier = Modifier,
    viewModel: ShardViewModel = hiltViewModel(),
) {
    val state by viewModel.state.collectAsStateWithLifecycle()
    val feed by viewModel.feed.collectAsStateWithLifecycle()
    val connected by viewModel.connected.collectAsStateWithLifecycle()
    val features by viewModel.shardFeatures.collectAsStateWithLifecycle()

    when (val s = state) {
        is UiState.Loading -> LoadingView(modifier)
        is UiState.Error -> ErrorView(s.kind, onRetry = viewModel::load, modifier = modifier)
        is UiState.Success -> HubContent(
            hub = s.data,
            feed = feed,
            connected = connected,
            features = features,
            onOpenBoard = onOpenBoard,
            modifier = modifier,
        )
    }
}

@Composable
private fun HubContent(
    hub: ShardHub,
    feed: List<FeedLine>,
    connected: Boolean,
    features: ShardFeatures?,
    onOpenBoard: (ShardBoard) -> Unit,
    modifier: Modifier = Modifier,
) {
    LazyColumn(
        modifier = modifier.fillMaxSize().padding(horizontal = 16.dp),
        verticalArrangement = Arrangement.spacedBy(12.dp),
        contentPadding = androidx.compose.foundation.layout.PaddingValues(vertical = 16.dp),
    ) {
        item { StatusCard(hub.status, hub.presence?.count) }
        item { BoardsCard(features, onOpenBoard) }

        if (hub.online.isNotEmpty()) {
            item { SectionHeader(stringResource(R.string.shard_section_staff)) }
            items(hub.online, key = { it.serial ?: it.name.hashCode().toLong() }) { staff ->
                StaffRow(staff)
            }
        }

        item {
            Row(
                Modifier.fillMaxWidth().padding(top = 4.dp),
                horizontalArrangement = Arrangement.SpaceBetween,
                verticalAlignment = Alignment.CenterVertically,
            ) {
                SectionHeader(stringResource(R.string.shard_section_activity))
                LiveChip(connected)
            }
        }
        if (feed.isEmpty()) {
            item {
                Text(
                    stringResource(R.string.shard_feed_empty),
                    style = MaterialTheme.typography.bodyMedium,
                    color = MaterialTheme.colorScheme.onSurfaceVariant,
                    modifier = Modifier.padding(vertical = 8.dp),
                )
            }
        } else {
            items(feed, key = { it.id }) { line -> FeedRow(line) }
        }
    }
}

@Composable
private fun StatusCard(status: ShardStatusDto, presenceCount: Int?) {
    FeatureCard {
        Row(
            Modifier.fillMaxWidth(),
            horizontalArrangement = Arrangement.SpaceBetween,
            verticalAlignment = Alignment.CenterVertically,
        ) {
            Column(Modifier.weight(1f)) {
                Text(
                    text = stringResource(
                        if (status.isOnline) R.string.shard_status_online else R.string.shard_status_offline,
                    ),
                    style = MaterialTheme.typography.titleMedium,
                    color = MaterialTheme.colorScheme.onSurface,
                )
                val online = presenceCount ?: status.onlineCount
                Text(
                    text = stringResource(R.string.shard_online_count, online),
                    style = MaterialTheme.typography.labelMedium,
                    color = MaterialTheme.colorScheme.onSurfaceVariant,
                    modifier = Modifier.padding(top = 6.dp),
                )
                status.economy?.gold?.let { gold ->
                    Text(
                        text = stringResource(R.string.shard_economy_gold, "%,.0f".format(gold)),
                        style = MaterialTheme.typography.bodySmall,
                        color = MaterialTheme.colorScheme.onSurfaceVariant,
                        modifier = Modifier.padding(top = 2.dp),
                    )
                }
            }
            StatusPill(
                text = stringResource(
                    if (status.isOnline) R.string.shard_live else R.string.shard_offline,
                ),
                tone = if (status.isOnline) PillTone.Success else PillTone.Neutral,
            )
        }
    }
}

@Composable
private fun BoardsCard(features: ShardFeatures?, onOpenBoard: (ShardBoard) -> Unit) {
    val boards = visibleBoards(features).map { board ->
        board to when (board) {
            ShardBoard.CHAMPS -> R.string.shard_nav_champs
            ShardBoard.GUILDS -> R.string.shard_nav_guilds
            ShardBoard.GOVERNORS -> R.string.shard_nav_governors
            ShardBoard.HOUSES -> R.string.shard_nav_houses
        }
    }
    if (boards.isEmpty()) return
    ShardCard(Modifier.fillMaxWidth()) {
        Column {
            boards.forEachIndexed { index, (board, labelRes) ->
                Text(
                    text = stringResource(labelRes),
                    style = MaterialTheme.typography.bodyLarge,
                    modifier = Modifier
                        .fillMaxWidth()
                        .clickable { onOpenBoard(board) }
                        .padding(horizontal = 16.dp, vertical = 14.dp),
                )
                if (index < boards.lastIndex) HorizontalDivider()
            }
        }
    }
}

@Composable
private fun SectionHeader(text: String) {
    SectionLabel(text = text, modifier = Modifier.padding(top = 4.dp))
}

@Composable
private fun StaffRow(staff: OnlineStaffDto) {
    Row(
        Modifier.fillMaxWidth().padding(vertical = 6.dp),
        verticalAlignment = Alignment.CenterVertically,
    ) {
        Text(
            text = staff.name ?: "—",
            style = MaterialTheme.typography.bodyMedium,
            modifier = Modifier.weight(1f),
        )
        staff.map?.let {
            Text(
                text = it,
                style = MaterialTheme.typography.labelSmall,
                color = MaterialTheme.colorScheme.onSurfaceVariant,
            )
        }
    }
}

@Composable
private fun FeedRow(line: FeedLine) {
    Text(
        text = line.text,
        style = MaterialTheme.typography.bodyMedium,
        maxLines = 2,
        overflow = TextOverflow.Ellipsis,
        modifier = Modifier.fillMaxWidth().padding(vertical = 6.dp),
    )
}
