package aquadx.net

import ext.*
import aquadx.net.db.AquaUserServices
import aquadx.net.utils.SUCCESS
import aquadx.sega.chusan.model.Chu3Repos
import aquadx.sega.general.model.Card
import aquadx.sega.general.model.CardStatus
import aquadx.sega.general.model.sensitiveInfo
import aquadx.sega.maimai2.model.Mai2Repos
import jakarta.transaction.Transactional
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty
import org.springframework.boot.context.properties.ConfigurationProperties
import org.springframework.context.annotation.Configuration
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RestController

@Configuration
@ConfigurationProperties(prefix = "aqua-net.bot")
class BotProps {
    var enabled: Boolean = false
    var secret: String = ""
}

@RestController
@ConditionalOnProperty("aqua-net.bot.enabled", havingValue = "true")
@API("/api/v2/bot")
class BotController(
    val us: AquaUserServices,
    val props: BotProps,
    val chu3Db: Chu3Repos,
    val mai2Db: Mai2Repos,
) {
    fun Str.checkSecret() {
        if (this != props.secret) 403 - "Invalid Secret"
    }

    @PostMapping("/ranking-ban")
    @Doc("Ban a user from the leaderboard", "Success status")
    suspend fun rankingBan(@RP secret: Str, @RP username: Str): Any {
        secret.checkSecret()

        return us.cardByName(username) { card ->
            card.rankingBanned = true
            us.cardRepo.save(card)

            SUCCESS
        }
    }

    @API("/migrated-to-minato")
    fun migratedToMinato(@RP secret: Str, @RP card: Str): Any {
        secret.checkSecret()

        // 1. Find user card
        val oc = (us.cardRepo.findByLuid(card) ?: (404 - "Card not found")).maybeGhost()

        // 2. Change the status to migrated
        us.cardRepo.save(oc.apply {
            status = CardStatus.MIGRATED_TO_MINATO
            accessTime = utcNow()
        })
        return SUCCESS
    }

    @API("/clear-migrate-flag")
    fun clearMigrateFlag(@RP secret: Str, @RP card: Str): Any {
        secret.checkSecret()

        val oc = (us.cardRepo.findByLuid(card) ?: (404 - "Card not found")).maybeGhost()

        us.cardRepo.save(oc.apply {
            status = CardStatus.NORMAL
            accessTime = utcNow()
        })
        return SUCCESS
    }

    @Transactional
    @PostMapping("/debug-user-profile")
    @Doc("Obtain debug information for a user card", "User card details")
    fun debugUserProfile(@RP secret: Str, @RP cardId: Str): Any {
        secret.checkSecret()

        // 1. Check if the card exist
        var cards: MutableList<Card> = listOfNotNull(
            us.cardRepo.findByLuid(cardId),
        ).mut

        cardId.toLongOrNull()?.let {
            cards += listOfNotNull(
                us.cardRepo.findById(it)(),
                us.cardRepo.findByExtId(it),
            )

            cards += listOfNotNull(
                us.userRepo.findByAuId(it)?.cards,
            ).flatten()
        }

        cards += listOfNotNull(
            us.userRepo.findByEmailIgnoreCase(cardId)?.cards,
            us.userRepo.findByUsernameIgnoreCase(cardId)?.cards,
        ).flatten()

        cards += cards.flatMap {
            (it.aquaUser?.cards ?: emptyList()) + listOfNotNull(it.aquaUser?.ghostCard)
        }

        cards = cards.distinctBy { it.id }.mut

        return cards.map { card ->
            // Find all games played by this card
            val chu3 = chu3Db.userData.findByCard_ExtId(card.extId)
            val mai2 = mai2Db.userData.findByCard_ExtId(card.extId)
            val gamesDict = listOfNotNull(chu3, mai2).map {
                // Find the keychip owner
                val keychip = it.lastClientId
                val keychipOwner = keychip?.let { us.userRepo.findByKeychip(it) }

                mapOf(
                    "userData" to it,
                    "keychip" to keychip,
                    "keychipOwner" to keychipOwner,
                    "keychipOwnerCards" to keychipOwner?.cards?.map { it.sensitiveInfo() }
                )
            }

            mapOf(
                "card" to card.sensitiveInfo(),
                "games" to gamesDict
            )
        }
    }
}
