/*
 * SPDX-License-Identifier: GPL-3.0-or-later
 */
package com.runicgateway.app.data.api.dto

import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.JsonElement

/**
 * Wire shapes for the M10 staff-operations surface over `/api/v1/admin/…` (PLAN.md
 * §1, §6.4). These are consumed only by the staff screens (dashboard, moderation,
 * support, content); every DTO ignores unknown keys (NetworkModule's lenient Json)
 * so additive backend fields stay safe. Nothing here is auto-provisioned or secret.
 */

/** `GET /admin/dashboard` — the staff landing summary. */
@Serializable
data class AdminDashboardDto(
    @SerialName("site_mode") val siteMode: String = "live",
    @SerialName("last_change") val lastChange: SiteModeChangeDto = SiteModeChangeDto(),
    val counts: AdminCountsDto = AdminCountsDto(),
    @SerialName("recent_activity") val recentActivity: List<AdminActivityDto> = emptyList(),
)

@Serializable
data class SiteModeChangeDto(
    val at: String? = null,
    val by: String? = null,
)

@Serializable
data class AdminCountsDto(
    /** Post counts keyed by DB category (`news`, `five_on_friday`, …). */
    val posts: Map<String, Int> = emptyMap(),
    val users: Int = 0,
)

/** One row of the recent admin-activity log. `detail` is provider-shaped JSON. */
@Serializable
data class AdminActivityDto(
    val id: Long = 0,
    val username: String? = null,
    val action: String = "",
    val detail: JsonElement? = null,
    @SerialName("created_at") val createdAt: String? = null,
)

/** `PUT /admin/site-mode` request + response. */
@Serializable
data class SiteModeRequest(val mode: String)

@Serializable
data class SiteModeStateDto(
    @SerialName("site_mode") val siteMode: String = "live",
    @SerialName("changed_at") val changedAt: String? = null,
    @SerialName("changed_by") val changedBy: String? = null,
)

// ── Content: news posts ───────────────────────────────────────────────────

/**
 * A post row from `GET /admin/posts` (all posts, incl. unpublished — unlike the
 * public feed). `published` is a 0/1 flag (MariaDB tinyint), exposed as [isPublished].
 */
@Serializable
data class AdminPostDto(
    val id: Long,
    val category: String = "",
    val title: String = "",
    val slug: String? = null,
    val excerpt: String? = null,
    val body: String? = null,
    @SerialName("image_url") val imageUrl: String? = null,
    val published: Int = 0,
    @SerialName("published_at") val publishedAt: String? = null,
    @SerialName("created_at") val createdAt: String? = null,
) {
    val isPublished: Boolean get() = published != 0
}

/** `POST/PUT /admin/posts` body. `category` is a URL category the backend maps
 *  (news | five-on-friday | newsletter | screenshots). */
@Serializable
data class PostCreateRequest(
    val category: String,
    val title: String,
    val excerpt: String? = null,
    val body: String? = null,
    @SerialName("image_url") val imageUrl: String? = null,
    val published: Boolean = false,
)

/** `PATCH /admin/posts/:id/publish` body. */
@Serializable
data class PublishRequest(val published: Boolean)

// ── Content: wiki taxonomy ────────────────────────────────────────────────

/** A wiki category from `GET /admin/wiki/categories` (with page counts). */
@Serializable
data class AdminWikiCategoryDto(
    val id: Long,
    val slug: String = "",
    val title: String = "",
    val description: String? = null,
    @SerialName("sort_order") val sortOrder: Int? = null,
    @SerialName("page_count") val pageCount: Int? = null,
    @SerialName("published_count") val publishedCount: Int? = null,
)

/** `POST /admin/wiki/categories` body. */
@Serializable
data class WikiCategoryRequest(
    val slug: String,
    val title: String,
    val description: String? = null,
    @SerialName("sort_order") val sortOrder: Int? = null,
)

/** A wiki tag from `GET /admin/wiki/tags` (tags derive from pages; read-only here). */
@Serializable
data class AdminWikiTagDto(
    val id: Long,
    val slug: String = "",
    val label: String = "",
    @SerialName("published_count") val publishedCount: Int? = null,
)

// ── Moderation (admin/moderator; shard write plane) ───────────────────────

/** `POST /admin/shard/kick` — at least one of account/serial. */
@Serializable
data class KickRequest(val account: String? = null, val serial: String? = null)

/** `POST /admin/shard/ban` — account/serial + optional duration (0/absent = indefinite). */
@Serializable
data class BanRequest(
    val account: String? = null,
    val serial: String? = null,
    @SerialName("durationSec") val durationSec: Long? = null,
    val reason: String? = null,
)

/** `POST /admin/shard/unban`. */
@Serializable
data class UnbanRequest(val account: String)

/** `POST /admin/shard/broadcast` — a system message to everyone online. */
@Serializable
data class BroadcastRequest(val text: String, val hue: Int? = null)

// ── Support queue (admin/moderator; help pages) ───────────────────────────

/**
 * One open help page from `GET /admin/shard/pages` (INTEGRATION.md §4). `pageId`
 * is the sender's in-game serial (the `:id` for respond/close). Permissive — the
 * shard-state fields beyond these (coords, timing) are ignored.
 */
@Serializable
data class SupportPageDto(
    @SerialName("pageId") val pageId: String = "",
    val type: String? = null,
    val message: String? = null,
    val handled: Boolean? = null,
    val handler: String? = null,
    val sender: SupportActorDto? = null,
)

/** The page's sender (actor object); [account] present when the character is linked. */
@Serializable
data class SupportActorDto(
    val name: String? = null,
    val account: String? = null,
)

/** `POST /admin/shard/pages/:id/respond` — reply, optionally closing the page. */
@Serializable
data class PageRespondRequest(val message: String, val close: Boolean = false)
