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

import android.Manifest
import android.os.Build
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Switch
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.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontStyle
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.NotificationStreamDto
import com.runicgateway.app.ui.UiState
import com.runicgateway.app.ui.components.EmptyView
import com.runicgateway.app.ui.components.ErrorView
import com.runicgateway.app.ui.components.LoadingView
import com.runicgateway.app.ui.components.SectionLabel

/**
 * The Notifications settings screen (PLAN.md §11, M7 Part 2 work item 6): the
 * subscribable catalog with per-stream toggles. Personal streams are greyed until a
 * game account is linked; turning a stream on requests the POST_NOTIFICATIONS
 * permission (API 33+) and registers the device, turning them all off unregisters it.
 */
@Composable
fun NotificationsScreen(
    modifier: Modifier = Modifier,
    viewModel: NotificationsViewModel = hiltViewModel(),
) {
    val state by viewModel.state.collectAsStateWithLifecycle()
    val context = LocalContext.current

    // Ask once for POST_NOTIFICATIONS when the user first enables a stream (API 33+).
    val permissionLauncher = rememberLauncherForActivityResult(
        ActivityResultContracts.RequestPermission(),
    ) { /* granted or not, the subscription is already saved server-side */ }

    fun ensureNotificationPermission() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
            permissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS)
        }
    }

    Column(
        modifier = modifier
            .fillMaxSize()
            .verticalScroll(rememberScrollState())
            .padding(16.dp),
    ) {
        Text(
            text = stringResource(R.string.notifications_title),
            style = MaterialTheme.typography.titleLarge,
        )
        Spacer(Modifier.height(4.dp))
        Text(
            text = stringResource(R.string.notifications_subtitle),
            style = MaterialTheme.typography.bodyMedium,
            color = MaterialTheme.colorScheme.onSurfaceVariant,
        )
        Spacer(Modifier.height(16.dp))

        if (!state.supported) {
            EmptyView(message = stringResource(R.string.notifications_unsupported))
            return@Column
        }

        state.feedback?.let { fb ->
            Text(
                text = stringResource(fb.messageRes),
                style = MaterialTheme.typography.bodyMedium,
                color = if (fb.ok) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.error,
                modifier = Modifier.padding(bottom = 12.dp),
            )
        }

        when (val catalog = state.catalog) {
            is UiState.Loading -> LoadingView()
            is UiState.Error -> ErrorView(kind = catalog.kind, onRetry = viewModel::load)
            is UiState.Success -> StreamList(
                streams = catalog.data,
                subscribed = state.subscribed,
                hasLinkedAccount = state.hasLinkedAccount,
                busy = state.busy,
                onToggle = { stream, on ->
                    if (on) ensureNotificationPermission()
                    viewModel.setSubscribed(stream, on)
                },
            )
        }
    }
}

@Composable
private fun StreamList(
    streams: List<NotificationStreamDto>,
    subscribed: Set<String>,
    hasLinkedAccount: Boolean,
    busy: Boolean,
    onToggle: (NotificationStreamDto, Boolean) -> Unit,
) {
    if (streams.isEmpty()) {
        EmptyView(message = stringResource(R.string.notifications_empty))
        return
    }
    val (personal, general) = streams.partition { it.personal }

    if (general.isNotEmpty()) {
        SectionLabel(stringResource(R.string.notifications_section_general))
        Spacer(Modifier.height(8.dp))
        general.forEach { stream ->
            StreamRow(stream, subscribed.contains(stream.id), enabled = !busy, hint = null) { on ->
                onToggle(stream, on)
            }
            HorizontalDivider()
        }
        Spacer(Modifier.height(20.dp))
    }

    if (personal.isNotEmpty()) {
        SectionLabel(stringResource(R.string.notifications_section_personal))
        Spacer(Modifier.height(8.dp))
        personal.forEach { stream ->
            val selectable = streamSelectable(stream, hasLinkedAccount)
            val hint = if (!selectable) stringResource(R.string.notifications_requires_link) else null
            StreamRow(stream, subscribed.contains(stream.id) && selectable, enabled = !busy && selectable, hint = hint) { on ->
                onToggle(stream, on)
            }
            HorizontalDivider()
        }
    }
}

@Composable
private fun StreamRow(
    stream: NotificationStreamDto,
    checked: Boolean,
    enabled: Boolean,
    hint: String?,
    onToggle: (Boolean) -> Unit,
) {
    Row(
        modifier = Modifier.fillMaxWidth().padding(vertical = 12.dp),
        verticalAlignment = Alignment.CenterVertically,
    ) {
        Column(modifier = Modifier.weight(1f).padding(end = 12.dp)) {
            Text(
                text = stream.label,
                style = MaterialTheme.typography.bodyLarge,
                color = if (enabled) MaterialTheme.colorScheme.onSurface else MaterialTheme.colorScheme.onSurfaceVariant,
            )
            Text(
                text = hint ?: stream.description,
                style = MaterialTheme.typography.bodySmall,
                color = MaterialTheme.colorScheme.onSurfaceVariant,
                fontStyle = if (hint != null) FontStyle.Italic else FontStyle.Normal,
            )
        }
        Switch(checked = checked, onCheckedChange = onToggle, enabled = enabled)
    }
}
