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

import androidx.annotation.StringRes
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.runicgateway.app.R
import com.runicgateway.app.core.result.ApiResult
import com.runicgateway.app.data.api.dto.AdminPostDto
import com.runicgateway.app.data.api.dto.PostCreateRequest
import com.runicgateway.app.data.api.dto.AdminWikiCategoryDto
import com.runicgateway.app.data.api.dto.WikiCategoryRequest
import com.runicgateway.app.data.api.dto.AdminWikiTagDto
import com.runicgateway.app.data.repository.AdminRepository
import com.runicgateway.app.ui.UiState
import com.runicgateway.app.ui.toUiState
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import javax.inject.Inject

/**
 * Drives the staff content screen (PLAN.md §1, M10): news posts (list, create,
 * publish/unpublish, delete) and wiki taxonomy (list categories/tags, create/delete
 * category). Any staff role reaches these (`staffOnly`); the full CMS block/hero
 * editor stays out of scope. Reads go through the typed [AdminRepository] (§7).
 */
@HiltViewModel
class AdminContentViewModel @Inject constructor(
    private val admin: AdminRepository,
) : ViewModel() {

    /** The valid URL categories the backend maps (posts.model CATEGORY_MAP keys). */
    val postCategories = listOf("news", "five-on-friday", "newsletter", "screenshots")

    data class Feedback(val ok: Boolean, @param:StringRes val messageRes: Int)

    data class State(
        val posts: UiState<List<AdminPostDto>> = UiState.Loading,
        val categories: UiState<List<AdminWikiCategoryDto>> = UiState.Loading,
        val tags: List<AdminWikiTagDto> = emptyList(),
        val busy: Boolean = false,
        val feedback: Feedback? = null,
    )

    private val _state = MutableStateFlow(State())
    val state: StateFlow<State> = _state.asStateFlow()

    init {
        loadPosts()
        loadWiki()
    }

    fun clearFeedback() = _state.update { it.copy(feedback = null) }

    fun loadPosts() {
        _state.update { it.copy(posts = UiState.Loading) }
        viewModelScope.launch { _state.update { it.copy(posts = admin.posts().toUiState()) } }
    }

    fun loadWiki() {
        _state.update { it.copy(categories = UiState.Loading) }
        viewModelScope.launch {
            _state.update { it.copy(categories = admin.wikiCategories().toUiState()) }
            when (val tags = admin.wikiTags()) {
                is ApiResult.Ok -> _state.update { it.copy(tags = tags.data) }
                else -> Unit // tags are secondary; leave the last list on a failure
            }
        }
    }

    fun togglePublish(post: AdminPostDto) = mutate(onSuccess = ::loadPosts) {
        admin.setPostPublished(post.id, !post.isPublished).asFeedback(R.string.admin_content_post_updated)
    }

    fun deletePost(id: Long) = mutate(onSuccess = ::loadPosts) {
        admin.deletePost(id).asFeedback(R.string.admin_content_post_deleted)
    }

    fun createPost(category: String, title: String, excerpt: String, body: String, published: Boolean) {
        if (title.isBlank()) {
            _state.update { it.copy(feedback = Feedback(false, R.string.admin_content_title_required)) }
            return
        }
        mutate(onSuccess = ::loadPosts) {
            admin.createPost(
                PostCreateRequest(
                    category = category,
                    title = title.trim(),
                    excerpt = excerpt.ifBlank { null },
                    body = body.ifBlank { null },
                    published = published,
                ),
            ).asFeedback(R.string.admin_content_post_created)
        }
    }

    fun createCategory(slug: String, title: String, description: String, sortOrder: Int?) {
        if (slug.isBlank() || title.isBlank()) {
            _state.update { it.copy(feedback = Feedback(false, R.string.admin_content_cat_fields_required)) }
            return
        }
        mutate(onSuccess = ::loadWiki) {
            admin.createWikiCategory(
                WikiCategoryRequest(slug.trim(), title.trim(), description.ifBlank { null }, sortOrder),
            ).asFeedback(R.string.admin_content_cat_created)
        }
    }

    fun deleteCategory(id: Long) = mutate(onSuccess = ::loadWiki) {
        admin.deleteWikiCategory(id).asFeedback(R.string.admin_content_cat_deleted)
    }

    // ── Shared mutation plumbing ──────────────────────────────────────────

    /** Run a write: set busy + clear feedback, then on completion set the feedback
     *  banner and, only if it succeeded, run [onSuccess] (a targeted reload). */
    private fun mutate(onSuccess: () -> Unit = {}, block: suspend () -> Feedback) {
        if (_state.value.busy) return
        _state.update { it.copy(busy = true, feedback = null) }
        viewModelScope.launch {
            val feedback = block()
            if (feedback.ok) onSuccess()
            _state.update { it.copy(busy = false, feedback = feedback) }
        }
    }

    /** Map an [ApiResult] to a [Feedback], with role/permission-aware failure copy. */
    private fun ApiResult<*>.asFeedback(@StringRes okRes: Int): Feedback = when (this) {
        is ApiResult.Ok -> Feedback(true, okRes)
        is ApiResult.HttpError ->
            Feedback(false, if (status == 403) R.string.admin_forbidden else R.string.admin_action_failed)
        is ApiResult.NetworkError -> Feedback(false, R.string.error_network)
    }
}
