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

import com.runicgateway.app.data.api.PublicApi
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.OnlineStaffDto
import com.runicgateway.app.data.api.dto.PageDto
import com.runicgateway.app.data.api.dto.PostDto
import com.runicgateway.app.data.api.dto.PresenceDto
import com.runicgateway.app.data.api.dto.SettingsDto
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.MarketMetaDto
import com.runicgateway.app.data.api.dto.MarketPageDto
import com.runicgateway.app.data.api.dto.MarketVendorDto
import com.runicgateway.app.data.api.dto.PointsBoardDto
import com.runicgateway.app.data.api.dto.RulesetDto
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

/**
 * A configurable fake of [PublicApi] for repository/ViewModel tests. Set the
 * relevant `var` to the body a call should return; set [error] to make every call
 * throw (drives the `ApiResult.HttpError` / `NetworkError` paths). Defaults are
 * empty/neutral so a call an assertion doesn't care about never crashes.
 */
class FakePublicApi : PublicApi {

    /** When non-null, every call throws this (use `httpError(code)` or an IOException). */
    var error: Throwable? = null

    var status: StatusDto = StatusDto()
    var settings: SettingsDto = SettingsDto()
    var posts: List<PostDto> = emptyList()
    var post: PostDto = PostDto(id = 0)
    var page: PageDto = PageDto(id = 0)
    var wikiPages: List<WikiSummaryDto> = emptyList()
    var wikiCategories: List<WikiCategoryDto> = emptyList()
    var wikiTags: List<WikiTagDto> = emptyList()
    var wikiPage: WikiPageDto = WikiPageDto(id = 0)
    var contactResponse: ContactResponse = ContactResponse(sent = true)
    var shardStatus: ShardStatusDto = ShardStatusDto()
    var shardFeed: List<FeedEventDto> = emptyList()
    var shardEconomy: List<EconomySampleDto> = emptyList()
    var shardOnline: List<OnlineStaffDto> = emptyList()
    var shardPresence: PresenceDto = PresenceDto()
    var champs: List<ChampDto> = emptyList()
    var guilds: List<GuildDto> = emptyList()
    var governors: List<GovernorDto> = emptyList()
    var governorHistory: List<GovernorTermDto> = emptyList()
    var houses: List<HouseDto> = emptyList()
    var shardFeatures: ShardFeaturesDto = ShardFeaturesDto()

    // Protocol 3.0 content (M11). `ruleset` is nullable on the wire: null means the
    // shard has never published one, which is a success, not a failure.
    var ruleset: RulesetDto? = null
    var pointsBoards: List<PointsBoardDto> = emptyList()
    var pointsBoard: PointsBoardDto = PointsBoardDto()
    var market: MarketPageDto = MarketPageDto()
    var marketMeta: MarketMetaDto = MarketMetaDto()
    var marketVendor: MarketVendorDto = MarketVendorDto()
    var atlasCreatures: AtlasCreaturePageDto = AtlasCreaturePageDto()
    var atlasCreature: AtlasCreatureDto = AtlasCreatureDto()
    var atlasMeta: AtlasMetaDto = AtlasMetaDto()

    /** Last market query seen, so a test can assert blanks were dropped. */
    var lastMarketQuery: String? = null

    /** Last atlas facet filter seen. */
    var lastAtlasFacet: String? = null

    /** Last contact request body seen (so a test can assert it was trimmed/forwarded). */
    var lastContact: ContactRequest? = null

    private fun <T> reply(value: T): T {
        error?.let { throw it }
        return value
    }

    override suspend fun probeStatus(absoluteStatusUrl: String): StatusDto = reply(status)
    override suspend fun getStatus(): StatusDto = reply(status)
    override suspend fun getSettings(): SettingsDto = reply(settings)

    override suspend fun getPosts(category: String): List<PostDto> = reply(posts)
    override suspend fun getPost(category: String, idOrSlug: String): PostDto = reply(post)
    override suspend fun getPage(slug: String): PageDto = reply(page)

    override suspend fun getWikiPages(query: String?, category: String?, tag: String?): List<WikiSummaryDto> =
        reply(wikiPages)
    override suspend fun getWikiCategories(): List<WikiCategoryDto> = reply(wikiCategories)
    override suspend fun getWikiTags(): List<WikiTagDto> = reply(wikiTags)
    override suspend fun getWikiPage(slug: String): WikiPageDto = reply(wikiPage)

    override suspend fun postContact(body: ContactRequest): ContactResponse {
        lastContact = body
        return reply(contactResponse)
    }

    override suspend fun getShardFeatures(): ShardFeaturesDto = reply(shardFeatures)
    override suspend fun getShardRuleset(): RulesetDto? = reply(ruleset)
    override suspend fun getShardPoints(): List<PointsBoardDto> = reply(pointsBoards)
    override suspend fun getShardPointsBoard(system: String): PointsBoardDto = reply(pointsBoard)

    override suspend fun getShardMarket(
        query: String?,
        minPrice: Long?,
        maxPrice: Long?,
        map: String?,
        region: String?,
        sort: String?,
        limit: Int?,
        offset: Int?,
    ): MarketPageDto {
        lastMarketQuery = query
        return reply(market)
    }

    override suspend fun getShardMarketMeta(): MarketMetaDto = reply(marketMeta)
    override suspend fun getShardMarketVendor(serial: String, limit: Int?, offset: Int?): MarketVendorDto =
        reply(marketVendor)

    override suspend fun getAtlasCreatures(
        query: String?,
        facet: String?,
        limit: Int?,
        offset: Int?,
    ): AtlasCreaturePageDto {
        lastAtlasFacet = facet
        return reply(atlasCreatures)
    }

    override suspend fun getAtlasCreature(slug: String): AtlasCreatureDto = reply(atlasCreature)
    override suspend fun getAtlasMeta(): AtlasMetaDto = reply(atlasMeta)
    override suspend fun getShardStatus(): ShardStatusDto = reply(shardStatus)
    override suspend fun getShardFeed(kind: String?, limit: Int?): List<FeedEventDto> = reply(shardFeed)
    override suspend fun getShardEconomy(limit: Int?): List<EconomySampleDto> = reply(shardEconomy)
    override suspend fun getShardOnline(): List<OnlineStaffDto> = reply(shardOnline)
    override suspend fun getShardPresence(): PresenceDto = reply(shardPresence)
    override suspend fun getShardChamps(): List<ChampDto> = reply(champs)
    override suspend fun getShardGuilds(): List<GuildDto> = reply(guilds)
    override suspend fun getShardGovernors(): List<GovernorDto> = reply(governors)
    override suspend fun getShardGovernorHistory(city: String, limit: Int?): List<GovernorTermDto> =
        reply(governorHistory)
    override suspend fun getShardHouses(): List<HouseDto> = reply(houses)
}
