package pw.hamzantal.mdranks

import hazae41.minecraft.kutils.bukkit.ConfigFile
import hazae41.minecraft.kutils.bukkit.keys
import hazae41.minecraft.kutils.get
import org.bukkit.ChatColor
import org.bukkit.configuration.ConfigurationSection
import org.bukkit.entity.Player
import pw.hamzantal.mdranks.api.Perms
import pw.hamzantal.mdranks.api.RankType
import pw.hamzantal.mdranks.api.Vault
import kotlin.math.floor
import kotlin.random.Random
import kotlin.random.nextInt

object Config : ConfigFile(MDRanks.pl.dataFolder["config.yml"]) {

	val codes = listOf(
		7 to "S",
		6 to "Q",
		5 to "q",
		4 to "T",
		3 to "B",
		2 to "M",
		1 to "K"
	)

	fun formatC(amount: Number): String {
		var base: Number = amount.toLong()
		var exponent = 0
		while (base.toLong() > 1000) {
			base = if (base.toLong() < 1_000_000) base.toDouble() / 1000
			else base.toLong() / 1000
			exponent++
		}
		val (_, suffix) = codes.firstOrNull { it.first <= exponent } ?: 0 to ""

		return config.getString("currencyFormat").replace("%amt%", "${floor(base.toDouble() * 10.0) / 10}$suffix")
	}

	fun wBrcks(msg: String): String {
		return config.getString("bracketformat", "&7[%display%&7]").replace("%display%", msg).c
	}

	val defaultPrestige by string("default_prestige")
	val defaultRebirth by string("default_rebirth")
}



object Ranks : ConfigFile(MDRanks.pl.dataFolder["ranks.yml"]) {
	init {
		autoSave = true
		minDelay = 1000
	}

	data class PlayerRank(val name: String, val regular: Int, val prestige: Int, val rebirth: Int)

	private const val cacheTime = 1000 * 60 * 5 //calculate top every 5 mins
	private var nextCalculate = 0L
	private var topCache = listOf<PlayerRank>()
	val top: List<PlayerRank> get() {
		if (nextCalculate < System.currentTimeMillis()) {
			topCache = config.keys.map {
				PlayerRank(
					config.getString("$it.name"),
					config.getInt("$it.regular"),
					config.getInt("$it.prestige"),
					config.getInt("$it.rebirth")
				)
			}.sortedWith(compareBy(PlayerRank::rebirth, PlayerRank::prestige, PlayerRank::regular))
				.reversed()
				.take(10)
			nextCalculate = System.currentTimeMillis() + cacheTime
		}
		return topCache
	}

	operator fun set(p: Player, path: String, value: Int) {
		this["${p.uniqueId}.$path"] = value
		this["${p.uniqueId}.name"] = p.name
	}
}

val String.wBrackets: String get() = Config.wBrcks(this)

open class RankData(
	val level: Int,
	val isLast: Boolean,
	val display: String,
	val cost: Double,
	val commandsOnUpgrade: List<String>,
	val broadcast: List<String>,
	val msg: List<String>,
	val addPerms: List<String>,
	val delPerms: List<String>,
	val others: ConfigurationSection
) {
	val randomCommandList: List<String>
		get() {
			val sec = others.getConfigurationSection("randomcmds") ?: return listOf()
			val chancePair = sec.getKeys(false)
				.map { sec.getInt("$it.chance") to sec.getStringList("$it.commands") }

			val max = chancePair.sumBy { it.first }
			val choice = Random.nextInt(1..max)
			var total = 0
			return chancePair.first {
				total += it.first
				total >= choice
			}.second
		}

	fun cost(p: Player): Double {
		var calcCost = cost.toDouble()
		calcCost *= 1 + (RegularConfig(Perms.reg(p)).percentageIncrease / 100.0)
		if (Perms.prestige(p) != 0)
			calcCost *= 1 + (PrestigeConfig(Perms.prestige(p)).percentageIncrease / 100.0)
		if (Perms.rebirth(p) != 0)
			calcCost *= 1 + (RebirthConfig(Perms.rebirth(p)).percentageIncrease / 100.0)
		return calcCost
	}

	val percentageIncrease: Double get() = others.getDouble("cost_percentage_increase", 0.0)

	override fun toString() =
		"RankData(level=$level, display='$display', cost=$cost, commandsOnUpgrade=$commandsOnUpgrade, broadcast=$broadcast, msg=$msg, addPerms=$addPerms, delPerms=$delPerms)"

}

abstract class PrisonConfig(fName: String) : ConfigFile(MDRanks.pl.dataFolder["$fName.yml"]) {

	fun nextPcnt(p: Player, type: RankType): Double {
		val current = this(p, type)
		if (current?.isLast == true) return 0.0
		val data = this(current?.level ?: 0 + 1)
		return Vault.balance(p) / data.cost(p)
	}

	fun next(p: Player, type: RankType, block: RankData.() -> String): String {
		val current = this(p, type)
		if (current?.isLast == true) return when (type) {
			RankType.REG      -> "&euse /prestige!"
			RankType.PRESTIGE -> "&euse /rebirth!"
			RankType.REBIRTH  -> "&eNo more rebirths!"
		}
		return this(current?.level ?: 0 + 1).block()
	}

	operator fun invoke(p: Player, type: RankType): RankData? {
		val level = Perms.rankFor(p, type)
		return if (level == 0) null
		else this(level)
	}

	operator fun invoke(level: Int): RankData {
		val sec = this.config.getConfigurationSection("$level") ?: run {
			throw IllegalStateException("The specified level ($level) does not exist.")
		}
		val index = config.keys.indexOf("$level")
		val last = index + 1 == config.keys.size
		return RankData(
			level,
			last,
			sec.getString("display", "").c,
			sec.getDouble("cost", 0.0),
			sec.getStringList("commands").c,
			sec.getStringList("broadcast").c,
			sec.getStringList("msg").c,
			sec.getStringList("addpermission").c,
			sec.getStringList("delpermission").c,
			sec
		)
	}

	fun max() = config.getKeys(false).last().toInt()

	init {
		val cFile = MDRanks.pl.dataFolder["$fName.yml"]
		if (!cFile.exists()) {
			cFile.createNewFile()
		}
	}
}

object RegularConfig : PrisonConfig("regular") {
	init {
		val cFile = MDRanks.pl.dataFolder["regular.yml"]
		if (!cFile.exists()) {
			cFile.createNewFile()
			set("1.display", "&1A")
			set("1.cost", 0)
			set("1.addpermission", listOf("essentials.warp.1"))
			set("2.display", "&2A")
			set("2.cost", 25000)
			set(
				"2.commands",
				listOf(
					"[console] give %player% diamond 1",
					"[op] say i am executing this command as an operator",
					"[player] warp %rankup%"
				)
			)
			set("2.broadcast", listOf("&7%player% &eranked up to &7%rankup%", "&7-----------------"))
			set("2.msg", listOf("&9You got &b1 &bdiamond."))
			set("2.randomcmds.1.chance", 70)
			set("2.randomcmds.1.commands", listOf("[console] tell %player% this was executed 70% of the time"))
			set("2.randomcmds.2.chance", 30)
			set("2.randomcmds.2.commands", listOf("[console] tell %player% this was executed 30% of the time"))
			set("2.addpermission", listOf("essentials.warp.2"))
			set("2.delpermission", listOf("essentials.warp.1"))
		}
	}
}

object PrestigeConfig : PrisonConfig("prestige") {
	init {
		val cFile = MDRanks.pl.dataFolder["prestige.yml"]
		if (!cFile.exists()) {
			cFile.createNewFile()
			set("1.display", "&1P1")
			set("1.cost", 25000)
			set("1.commands", listOf("[console] give %player% diamond 5"))
			set("1.broadcast", listOf("&7%player% &eprestiged up to &7%rankup%", "&7-----------------"))
			set("1.msg", listOf("&9You got &b5 &bdiamonds."))
			set("1.cost_percentage_increase", 25)

			set("2.display", "&2P2")
			set("2.cost", 50000)
			set("2.commands", listOf("[console] give %player% gold_ingot 10"))
			set("2.broadcast", listOf("&7%player% &eprestiged up to &7%rankup%", "&7-----------------"))
			set("2.msg", listOf("&9You got &b10 &6gold ingots."))
			set("2.cost_percentage_increase", 30)
		}
	}
}

object RebirthConfig : PrisonConfig("rebirth") {
	init {
		val cFile = MDRanks.pl.dataFolder["rebirth.yml"]
		if (!cFile.exists()) {
			cFile.createNewFile()
			set("1.display", "&1&lR1")
			set("1.cost", 100000)
			set("1.commands", listOf("[console] give %player% diamond 64"))
			set("1.broadcast", listOf("&7&l%player% &e&lprestiged up to &7&l%rankup%", "&7-----------------"))
			set("1.msg", listOf("&9You got &b5 &bdiamonds."))
			set("1.cost_percentage_increase", 25)
			set("1.cost_percentage_increase", 25)
		}
	}
}

val String.c: String get() = ChatColor.translateAlternateColorCodes('&', this)
val List<String>.c: List<String> get() = map { it.c }