package com.nisemoe.nise

import com.nisemoe.nise.integrations.CircleguardService
import kotlinx.serialization.Serializable
import java.time.OffsetDateTime
import java.util.UUID

data class UserQueueDetails(
    val isProcessing: Boolean,
    val lastCompletedUpdate: OffsetDateTime?,
    val canUpdate: Boolean,

    val progressCurrent: Int?,
    val progressTotal: Int?,
)

data class UserDetails(
    val user_id: Long,
    val username: String,
    val rank: Long?,
    val pp_raw: Double?,
    val join_date: String?,
    val seconds_played: Long?,
    val country: String?,
    val country_rank: Long?,
    val playcount: Long?,

    val suspicious_scores: Int = 0,
    val stolen_replays: Int = 0
)

@Serializable
@AllowCacheSerialization
data class Statistics(
    val total_beatmaps: Int,
    val total_users: Int,
    val total_scores: Int,
    val total_replay_scores: Int,
    val total_replay_similarity: Int
)

data class SuspiciousScoreEntry(
    val user_id: Long,
    val username: String,
    val replay_id: Long,
    val date: String,
    val beatmap_id: Long,
    val beatmap_beatmapset_id: Long,
    val beatmap_title: String,
    val beatmap_star_rating: Double,
    val pp: Double,
    val frametime: Double,
    val ur: Double
)

data class SimilarReplayEntry(
    val replay_id_1: Long,
    val replay_id_2: Long,
    val user_id_1: Long,
    val user_id_2: Long,
    val username_1: String,
    val username_2: String,
    val beatmap_beatmapset_id: Long,
    val replay_date_1: String,
    val replay_date_2: String,
    val replay_pp_1: Double,
    val replay_pp_2: Double,
    val beatmap_id: Long,
    val beatmap_title: String,
    val beatmap_star_rating: Double,
    val similarity: Double
)

data class ReplayPairStatistics(
    val similarity: Double,
    val correlation: Double,
)

data class ReplayPair(
    val replays: List<ReplayData>,
    val statistics: ReplayPairStatistics
)

data class ReplayDataChart(
    val title: String,
    val data: List<Double>
)

data class ReplayDataSimilarScore(
    val replay_id: Long,
    val user_id: Int,
    val username: String,
    val date: String,
    val pp: Double,
    val similarity: Double,
    val correlation: Double
)

data class ReplayViewerData(
    val beatmap: String,
    val replay: String,
    val mods: Int,
    val judgements: List<CircleguardService.ScoreJudgement>
)

data class ReplayPairViewerData(
    val beatmap: String,
    val replay1: String,
    val replay2: String,
    val mods: Int,
    val judgements1: List<CircleguardService.ScoreJudgement>,
    val judgements2: List<CircleguardService.ScoreJudgement>
)

data class UserReplayData(
    val replay_id: UUID,
    val osu_replay_id: Long?,
    val username: String,
    val beatmap_id: Int,
    val beatmap_beatmapset_id: Int,
    val beatmap_artist: String,
    val beatmap_title: String,
    val beatmap_star_rating: Double,
    val beatmap_creator: String,
    val beatmap_version: String,
    val score: Int,
    val mods: List<String>,
    val ur: Double?,
    val adjusted_ur: Double?,
    val frametime: Int,
    val snaps: Int,
    val hits: Int,

    var mean_error: Double?,
    var error_variance: Double?,
    var error_standard_deviation: Double?,
    var minimum_error: Double?,
    var maximum_error: Double?,
    var error_range: Double?,
    var error_coefficient_of_variation: Double?,
    var error_kurtosis: Double?,
    var error_skewness: Double?,

    var comparable_samples: Int? = null,
    var comparable_mean_error: Double? = null,
    var comparable_error_variance: Double? = null,
    var comparable_error_standard_deviation: Double? = null,
    var comparable_minimum_error: Double? = null,
    var comparable_maximum_error: Double? = null,
    var comparable_error_range: Double? = null,
    var comparable_error_coefficient_of_variation: Double? = null,
    var comparable_error_kurtosis: Double? = null,
    var comparable_error_skewness: Double? = null,

    val perfect: Boolean,
    val max_combo: Int,

    val count_300: Int,
    val count_100: Int,
    val count_50: Int,
    val count_miss: Int,

    val similar_scores: List<ReplayDataSimilarScore>,
    val error_distribution: Map<Int, DistributionEntry>,
    val charts: List<ReplayDataChart>
)

data class ReplayData(
    val replay_id: Long,
    val user_id: Int,
    val username: String,
    val date: String,
    val beatmap_id: Int,
    val beatmap_beatmapset_id: Int,
    val beatmap_artist: String,
    val beatmap_title: String,
    val beatmap_star_rating: Double,
    val beatmap_creator: String,
    val beatmap_version: String,
    val score: Int,
    val mods: List<String>,
    val rank: String,
    val ur: Double?,
    val adjusted_ur: Double?,
    val average_ur: Double?,
    val frametime: Int,
    val snaps: Int,
    val hits: Int,

    var mean_error: Double?,
    var error_variance: Double?,
    var error_standard_deviation: Double?,
    var minimum_error: Double?,
    var maximum_error: Double?,
    var error_range: Double?,
    var error_coefficient_of_variation: Double?,
    var error_kurtosis: Double?,
    var error_skewness: Double?,

    var comparable_samples: Int? = null,
    var comparable_mean_error: Double? = null,
    var comparable_error_variance: Double? = null,
    var comparable_error_standard_deviation: Double? = null,
    var comparable_minimum_error: Double? = null,
    var comparable_maximum_error: Double? = null,
    var comparable_error_range: Double? = null,
    var comparable_error_coefficient_of_variation: Double? = null,
    var comparable_error_kurtosis: Double? = null,
    var comparable_error_skewness: Double? = null,

    val pp: Double,
    val perfect: Boolean,
    val max_combo: Int,

    val count_300: Int,
    val count_100: Int,
    val count_50: Int,
    val count_miss: Int,

    val similar_scores: List<ReplayDataSimilarScore>,
    val error_distribution: Map<Int, DistributionEntry>,
    val charts: List<ReplayDataChart>
) {

    fun calculateAccuracy(): Double {
        if (count_300 + count_100 + count_50 + count_miss == 0) {
            return 0.0
        }

        val totalHits = count_300 + count_100 + count_50 + count_miss
        val accuracy = (300.0 * count_300 + 100.0 * count_100 + 50.0 * count_50) / (300.0 * totalHits)
        return accuracy * 100
    }

}

data class DistributionEntry(
    val countMiss: Double,
    val count300: Double,
    val count100: Double,
    val count50: Double
)