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

import com.runicgateway.app.data.api.dto.AtlasCreatureDto
import com.runicgateway.app.data.api.dto.AtlasCreaturePageDto
import com.runicgateway.app.data.api.dto.AtlasMetaDto
import com.runicgateway.app.data.api.dto.ChampDto
import com.runicgateway.app.data.api.dto.ContactRequest
import com.runicgateway.app.data.api.dto.ContactResponse
import com.runicgateway.app.data.api.dto.EconomySampleDto
import com.runicgateway.app.data.api.dto.FeedEventDto
import com.runicgateway.app.data.api.dto.GovernorDto
import com.runicgateway.app.data.api.dto.GovernorTermDto
import com.runicgateway.app.data.api.dto.GuildDto
import com.runicgateway.app.data.api.dto.HouseDto
import com.runicgateway.app.data.api.dto.MarketMetaDto
import com.runicgateway.app.data.api.dto.MarketPageDto
import com.runicgateway.app.data.api.dto.MarketVendorDto
import com.runicgateway.app.data.api.dto.OnlineStaffDto
import com.runicgateway.app.data.api.dto.PageDto
import com.runicgateway.app.data.api.dto.PointsBoardDto
import com.runicgateway.app.data.api.dto.PostDto
import com.runicgateway.app.data.api.dto.PresenceDto
import com.runicgateway.app.data.api.dto.RulesetDto
import com.runicgateway.app.data.api.dto.SettingsDto
import com.runicgateway.app.data.api.dto.ShardFeaturesDto
import com.runicgateway.app.data.api.dto.ShardStatusDto
import com.runicgateway.app.data.api.dto.StatusDto
import com.runicgateway.app.data.api.dto.WikiCategoryDto
import com.runicgateway.app.data.api.dto.WikiPageDto
import com.runicgateway.app.data.api.dto.WikiSummaryDto
import com.runicgateway.app.data.api.dto.WikiTagDto
import retrofit2.http.Body
import retrofit2.http.GET
import retrofit2.http.POST
import retrofit2.http.Path
import retrofit2.http.Query
import retrofit2.http.Url

/**
 * The public (unauthenticated) surface consumed in M1: site status/settings,
 * news posts, CMS pages, wiki, and the contact form (PLAN.md §6.1). Paths are
 * relative to the sentinel base host; [com.runicgateway.app.core.net.HostSelectionInterceptor]
 * retargets them onto the configured shard site. The public shard widgets (§6.2)
 * are added in M2; auth (§4) and player game data (§6.3) arrive in later milestones.
 * The live SSE stream (`/public/shard/stream`) is not a Retrofit call — it is
 * consumed via OkHttp in [com.runicgateway.app.core.net.ShardStreamClient].
 */
interface PublicApi {

    // ── First-run probe (absolute URL; bypasses host rewriting) ──────────
    /**
     * Validates a candidate site during the first-run connect flow (§3). Takes a
     * fully-qualified URL so the request carries a real host and is left
     * untouched by the host interceptor — the probe targets the URL the user
     * just typed, not the (not-yet-configured) base.
     */
    @GET
    suspend fun probeStatus(@Url absoluteStatusUrl: String): StatusDto

    // ── Site status / settings ───────────────────────────────────────────
    @GET("api/v1/public/status")
    suspend fun getStatus(): StatusDto

    @GET("api/v1/public/settings")
    suspend fun getSettings(): SettingsDto

    // ── News & content ───────────────────────────────────────────────────
    @GET("api/v1/public/posts/{category}")
    suspend fun getPosts(@Path("category") category: String): List<PostDto>

    @GET("api/v1/public/posts/{category}/{idOrSlug}")
    suspend fun getPost(
        @Path("category") category: String,
        @Path("idOrSlug") idOrSlug: String,
    ): PostDto

    @GET("api/v1/public/pages/{slug}")
    suspend fun getPage(@Path("slug") slug: String): PageDto

    // ── Wiki ─────────────────────────────────────────────────────────────
    @GET("api/v1/public/wiki")
    suspend fun getWikiPages(
        @Query("q") query: String? = null,
        @Query("category") category: String? = null,
        @Query("tag") tag: String? = null,
    ): List<WikiSummaryDto>

    @GET("api/v1/public/wiki/categories")
    suspend fun getWikiCategories(): List<WikiCategoryDto>

    @GET("api/v1/public/wiki/tags")
    suspend fun getWikiTags(): List<WikiTagDto>

    @GET("api/v1/public/wiki/{slug}")
    suspend fun getWikiPage(@Path("slug") slug: String): WikiPageDto

    // ── Contact ──────────────────────────────────────────────────────────
    @POST("api/v1/public/contact")
    suspend fun postContact(@Body body: ContactRequest): ContactResponse

    // ── Public shard widgets (§6.2) ──────────────────────────────────────
    /**
     * Which shard features this caller may reach, so the menu hides entries instead
     * of rendering links that 404/403 (§5, M11). Answered per-viewer: an anonymous
     * call and a signed-in one can differ.
     */
    @GET("api/v1/public/shard/features")
    suspend fun getShardFeatures(): ShardFeaturesDto

    @GET("api/v1/public/shard/status")
    suspend fun getShardStatus(): ShardStatusDto

    @GET("api/v1/public/shard/feed")
    suspend fun getShardFeed(
        @Query("kind") kind: String? = null,
        @Query("limit") limit: Int? = null,
    ): List<FeedEventDto>

    @GET("api/v1/public/shard/economy")
    suspend fun getShardEconomy(@Query("limit") limit: Int? = null): List<EconomySampleDto>

    @GET("api/v1/public/shard/online")
    suspend fun getShardOnline(): List<OnlineStaffDto>

    @GET("api/v1/public/shard/presence")
    suspend fun getShardPresence(): PresenceDto

    @GET("api/v1/public/shard/champs")
    suspend fun getShardChamps(): List<ChampDto>

    @GET("api/v1/public/shard/guilds")
    suspend fun getShardGuilds(): List<GuildDto>

    @GET("api/v1/public/shard/governors")
    suspend fun getShardGovernors(): List<GovernorDto>

    @GET("api/v1/public/shard/governors/{city}/history")
    suspend fun getShardGovernorHistory(
        @Path("city") city: String,
        @Query("limit") limit: Int? = null,
    ): List<GovernorTermDto>

    @GET("api/v1/public/shard/houses")
    suspend fun getShardHouses(): List<HouseDto>

    // ── Protocol 3.0 shard content (§9 M11) ──────────────────────────────
    //
    // Each of these sits behind the website's `requireFeature` gate: a 404 means the
    // shard doesn't publish it and a 403 means this viewer is below its audience rung,
    // which `toShardUiState()` folds into one "not available here" state.

    /** The shard's configured ruleset. A `null` body means "not published yet". */
    @GET("api/v1/public/shard/ruleset")
    suspend fun getShardRuleset(): RulesetDto?

    /** Every points/loyalty leaderboard the shard publishes. */
    @GET("api/v1/public/shard/points")
    suspend fun getShardPoints(): List<PointsBoardDto>

    @GET("api/v1/public/shard/points/{system}")
    suspend fun getShardPointsBoard(@Path("system") system: String): PointsBoardDto

    /**
     * Search the player-vendor index. **Rate-limited** — the first genuinely expensive
     * public endpoint on the site, so handle `429` (`ErrorKind.RATE_LIMITED`).
     */
    @GET("api/v1/public/shard/market")
    suspend fun getShardMarket(
        @Query("q") query: String? = null,
        @Query("minPrice") minPrice: Long? = null,
        @Query("maxPrice") maxPrice: Long? = null,
        @Query("map") map: String? = null,
        @Query("region") region: String? = null,
        @Query("sort") sort: String? = null,
        @Query("limit") limit: Int? = null,
        @Query("offset") offset: Int? = null,
    ): MarketPageDto

    /** Index size, staleness, and which facets/regions actually hold vendors. */
    @GET("api/v1/public/shard/market/meta")
    suspend fun getShardMarketMeta(): MarketMetaDto

    @GET("api/v1/public/shard/market/vendors/{serial}")
    suspend fun getShardMarketVendor(
        @Path("serial") serial: String,
        @Query("limit") limit: Int? = null,
        @Query("offset") offset: Int? = null,
    ): MarketVendorDto

    // The atlas lives under /public/atlas, NOT /public/shard: it is static shard
    // content parsed from the server's data files, so it stays readable while the
    // shard is down — but it IS site-mode gated, unlike the shard routes.
    @GET("api/v1/public/atlas/creatures")
    suspend fun getAtlasCreatures(
        @Query("q") query: String? = null,
        @Query("facet") facet: String? = null,
        @Query("limit") limit: Int? = null,
        @Query("offset") offset: Int? = null,
    ): AtlasCreaturePageDto

    @GET("api/v1/public/atlas/creatures/{slug}")
    suspend fun getAtlasCreature(@Path("slug") slug: String): AtlasCreatureDto

    @GET("api/v1/public/atlas/meta")
    suspend fun getAtlasMeta(): AtlasMetaDto
}
