package com.pixelized.desktop.lwa.network

import com.pixelized.desktop.lwa.repository.settings.SettingsRepository
import com.pixelized.shared.lwa.model.alteration.AlterationJson
import com.pixelized.shared.lwa.model.campaign.CampaignJson
import com.pixelized.shared.lwa.model.characterSheet.CharacterSheetJson
import com.pixelized.shared.lwa.model.inventory.InventoryJson
import com.pixelized.shared.lwa.model.item.ItemJson
import com.pixelized.shared.lwa.model.map.MapJson
import com.pixelized.shared.lwa.model.tag.TagJson
import com.pixelized.shared.lwa.protocol.rest.APIResponse
import com.pixelized.shared.lwa.protocol.rest.ApiPurseJson
import com.pixelized.shared.lwa.protocol.rest.CharacterPreviewJson
import io.ktor.client.HttpClient
import io.ktor.client.call.body
import io.ktor.client.request.delete
import io.ktor.client.request.get
import io.ktor.client.request.put
import io.ktor.client.request.setBody
import io.ktor.http.ContentType
import io.ktor.http.contentType

class LwaClientImpl(
    private val client: HttpClient,
    private val setting: SettingsRepository,
) : LwaClient {
    private val root get() = setting.settings().network.root

    @Throws
    override suspend fun getAlterations(): APIResponse<List<AlterationJson>> = client
        .get("$root/alteration/all")
        .body()

    @Throws
    override suspend fun getAlteration(
        alterationId: String,
    ): APIResponse<AlterationJson?> = client
        .get("$root/alteration/detail?alterationId=$alterationId")
        .body()

    @Throws
    override suspend fun putAlteration(
        alterationJson: AlterationJson,
        create: Boolean,
    ) = client
        .put("$root/alteration/update?create=$create") {
            contentType(ContentType.Application.Json)
            setBody(alterationJson)
        }
        .body<APIResponse<Unit>>()

    @Throws
    override suspend fun deleteAlteration(alterationId: String) = client
        .delete("$root/alteration/delete?alterationId=$alterationId")
        .body<APIResponse<Unit>>()

    @Throws
    override suspend fun getCampaign(): APIResponse<CampaignJson> = client
        .get("$root/campaign")
        .body()

    @Throws
    override suspend fun putCampaignScene(
        scene: CampaignJson.SceneJson,
    ): APIResponse<Unit> = client
        .put("$root/campaign/scene") {
            contentType(ContentType.Application.Json)
            setBody(scene)
        }
        .body<APIResponse<Unit>>()

    @Throws
    override suspend fun putCampaignCharacter(
        characterSheetId: String,
    ) = client
        .put("$root/campaign/character/add?characterSheetId=$characterSheetId")
        .body<APIResponse<Unit>>()

    @Throws
    override suspend fun removeCampaignCharacter(
        characterSheetId: String,
    ) = client
        .delete("$root/campaign/character/delete?characterSheetId=$characterSheetId")
        .body<APIResponse<Unit>>()

    @Throws
    override suspend fun putCampaignNpc(
        characterSheetId: String,
    ) = client
        .put("$root/campaign/npc/add?characterSheetId=$characterSheetId")
        .body<APIResponse<Unit>>()

    @Throws
    override suspend fun removeCampaignNpc(
        characterSheetId: String,
    ) = client
        .delete("$root/campaign/npc/delete?characterSheetId=$characterSheetId")
        .body<APIResponse<Unit>>()

    @Throws
    override suspend fun getCharacterPreviews(): APIResponse<List<CharacterPreviewJson>> = client
        .get("$root/character/preview/all")
        .body()

    @Throws
    override suspend fun getCharacterPreview(
        characterSheetId: String,
    ): APIResponse<CharacterPreviewJson> = client
        .get("$root/character/preview/detail?characterSheetId=$characterSheetId")
        .body()

    @Throws
    override suspend fun getCharacter(
        characterSheetId: String,
    ): APIResponse<CharacterSheetJson> = client
        .get("$root/character/detail?characterSheetId=$characterSheetId")
        .body()

    @Throws
    override suspend fun putCharacter(
        sheet: CharacterSheetJson,
        create: Boolean,
    ) = client
        .put("$root/character/update/sheet?create=$create") {
            contentType(ContentType.Application.Json)
            setBody(sheet)
        }
        .body<APIResponse<Unit>>()

    @Throws
    override suspend fun putCharacterDamage(
        characterSheetId: String,
        damage: Int,
    ) = client
        .put("$root/character/update/damage?characterSheetId=$characterSheetId&damage=$damage")
        .body<APIResponse<Unit>>()

    @Throws
    override suspend fun putCharacterFatigue(
        characterSheetId: String,
        fatigue: Int,
    ) = client
        .put("$root/character/update/fatigue?characterSheetId=$characterSheetId&fatigue=$fatigue")
        .body<APIResponse<Unit>>()

    @Throws
    override suspend fun putCharacterDiminished(
        characterSheetId: String,
        diminished: Int,
    ) = client
        .put("$root/character/update/diminished?characterSheetId=$characterSheetId&diminished=$diminished")
        .body<APIResponse<Unit>>()

    @Throws
    override suspend fun putCharacterAlteration(
        characterSheetId: String,
        alterationId: String,
        active: Boolean,
    ) = client
        .put("$root/character/update/alteration?characterSheetId=$characterSheetId&alterationId=$alterationId&active=$active")
        .body<APIResponse<Unit>>()

    @Throws
    override suspend fun deleteCharacterSheet(characterSheetId: String) = client
        .delete("$root/character/delete?characterSheetId=$characterSheetId")
        .body<APIResponse<Unit>>()

    @Throws
    override suspend fun getItems(): APIResponse<List<ItemJson>> = client
        .get("$root/item/all")
        .body()

    @Throws
    override suspend fun getItem(
        itemId: String,
    ): APIResponse<ItemJson> = client
        .get("$root/item/detail?itemId=$itemId")
        .body()

    @Throws
    override suspend fun putItem(
        item: ItemJson,
        create: Boolean,
    ): APIResponse<Unit> = client
        .put("$root/item/update?create=$create") {
            contentType(ContentType.Application.Json)
            setBody(item)
        }
        .body<APIResponse<Unit>>()

    @Throws
    override suspend fun deleteItem(
        itemId: String,
    ): APIResponse<Unit> = client
        .delete("$root/item/delete?itemId=$itemId")
        .body()

    @Throws
    override suspend fun getInventory(characterSheetId: String): APIResponse<InventoryJson> = client
        .get("$root/inventory/detail?characterSheetId=$characterSheetId")
        .body()

    @Throws
    override suspend fun putInventory(
        inventory: InventoryJson,
        create: Boolean,
    ): APIResponse<Unit> = client
        .put("$root/inventory/update?create=$create") {
            contentType(ContentType.Application.Json)
            setBody(inventory)
        }
        .body<APIResponse<Unit>>()

    @Throws
    override suspend fun putInventoryPurse(
        purse: ApiPurseJson,
    ): APIResponse<Unit> = client
        .put("$root/inventory/update/purse") {
            contentType(ContentType.Application.Json)
            setBody(purse)
        }
        .body<APIResponse<Unit>>()

    @Throws
    override suspend fun deleteInventory(characterSheetId: String): APIResponse<Unit> = client
        .delete("$root/inventory/delete?characterSheetId=$characterSheetId")
        .body()

    @Throws
    override suspend fun createInventoryItem(
        characterSheetId: String,
        itemId: String,
        count: Float,
    ): APIResponse<String> = client
        .put("$root/inventory/item/create?characterSheetId=$characterSheetId&itemId=$itemId&count=$count")
        .body<APIResponse<String>>()

    @Throws
    override suspend fun changeInventoryItemCount(
        characterSheetId: String,
        inventoryId: String,
        count: Float,
    ): APIResponse<Unit> = client
        .put("$root/inventory/item/count?characterSheetId=$characterSheetId&inventoryId=$inventoryId&count=$count")
        .body<APIResponse<Unit>>()

    @Throws
    override suspend fun equipInventoryItem(
        characterSheetId: String,
        inventoryId: String,
        equip: Boolean,
    ): APIResponse<Unit> = client
        .put("$root/inventory/item/equip?characterSheetId=$characterSheetId&inventoryId=$inventoryId&equip=$equip")
        .body<APIResponse<Unit>>()

    @Throws
    override suspend fun consumeInventoryItem(
        characterSheetId: String,
        inventoryId: String,
    ): APIResponse<Unit> = client
        .put("$root/inventory/item/consume?characterSheetId=$characterSheetId&inventoryId=$inventoryId")
        .body<APIResponse<Unit>>()

    @Throws
    override suspend fun deleteInventoryItem(
        characterSheetId: String,
        inventoryId: String,
    ): APIResponse<Unit> = client
        .delete("$root/inventory/item/delete?characterSheetId=$characterSheetId&inventoryId=$inventoryId")
        .body<APIResponse<Unit>>()

    @Throws
    override suspend fun getAlterationTags(): APIResponse<List<TagJson>> = client
        .get("$root/tag/alteration")
        .body()

    @Throws
    override suspend fun getCharacterTags(): APIResponse<List<TagJson>> = client
        .get("$root/tag/character")
        .body()

    @Throws
    override suspend fun getItemTags(): APIResponse<List<TagJson>> = client
        .get("$root/tag/item")
        .body()

    @Throws
    override suspend fun getMaps(): APIResponse<List<MapJson>> = client
        .get("$root/map/all")
        .body()

    @Throws
    override suspend fun getMap(mapId: String): APIResponse<MapJson> = client
        .get("$root/map/detail?mapId=$mapId")
        .body()

    @Throws
    override suspend fun putMap(
        mapJson: MapJson,
        create: Boolean,
    ): APIResponse<Unit> = client
        .put("$root/map/update?create=$create") {
            contentType(ContentType.Application.Json)
            setBody(mapJson)
        }
        .body<APIResponse<Unit>>()

    @Throws
    override suspend fun deleteMap(
        mapId: String,
    ): APIResponse<Unit> = client
        .delete("$root/map/delete?mapId=$mapId")
        .body<APIResponse<Unit>>()
}