package org.server_info.server_info

import net.kyori.adventure.text.Component
import org.bukkit.Bukkit
import org.bukkit.command.Command
import org.bukkit.command.CommandExecutor
import org.bukkit.command.CommandSender
import org.bukkit.entity.Player
import org.bukkit.plugin.java.JavaPlugin
import org.bukkit.scheduler.BukkitRunnable
import java.lang.management.ManagementFactory
import java.text.DecimalFormat

class Server_info : JavaPlugin() {

    private var lastTickTime = System.currentTimeMillis()
    private var tickCount = 0
    private val tickTimeList = ArrayList<Long>()
    private var tps = 20.0
    private val df = DecimalFormat("#.##")

    override fun onEnable() {
        // Register command
        getCommand("serverinfo")?.setExecutor(ServerInfoCommand(this))

        // Start server info collectors
        startTPSCalculation()
        startTabListUpdater()

        logger.info("ServerInfo plugin has been enabled!")
    }

    override fun onDisable() {
        logger.info("ServerInfo plugin has been disabled!")
    }

    private fun startTPSCalculation() {
        // Better TPS calculation method with moving average
        object : BukkitRunnable() {
            override fun run() {
                tickCount++

                val now = System.currentTimeMillis()
                val elapsed = now - lastTickTime
                lastTickTime = now

                // Store data point - time per tick in ms
                // Only add if we've gone through at least one tick to avoid startup anomalies
                if (tickCount > 1) {
                    tickTimeList.add(elapsed)
                }

                // Keep only the last 600 data points (30 seconds at 20 TPS)
                while (tickTimeList.size > 600) {
                    tickTimeList.removeAt(0)
                }

                // Calculate average tick time and TPS if we have data
                if (tickTimeList.isNotEmpty()) {
                    val avgTickTime = tickTimeList.average()
                    tps = 1000.0 / avgTickTime

                    // Clamp TPS between 0 and 20
                    if (tps > 20.0) tps = 20.0
                    if (tps < 0.0) tps = 0.0
                }
            }
        }.runTaskTimer(this, 1L, 1L) // Run every tick
    }

    private fun startTabListUpdater() {
        object : BukkitRunnable() {
            override fun run() {
                updateTabList()
            }
        }.runTaskTimer(this, 20L, 20L) // Update every second
    }

    private fun updateTabList() {
        val header = getTabListHeader()
        val footer = getTabListFooter()

        for (player in Bukkit.getOnlinePlayers()) {
            player.sendPlayerListHeaderAndFooter(header, footer)
        }
    }

    private fun getTabListHeader(): Component {
        val serverName = server.name
        return Component.text("§6§l$serverName\n§r§7Online players: §f${server.onlinePlayers.size}/${server.maxPlayers}")
    }

    private fun getTabListFooter(): Component {
        // Memory information
        val runtime = Runtime.getRuntime()
        val maxMemory = runtime.maxMemory() / (1024 * 1024)
        val allocatedMemory = runtime.totalMemory() / (1024 * 1024)
        val freeMemory = runtime.freeMemory() / (1024 * 1024)
        val usedMemory = allocatedMemory - freeMemory

        // CPU usage
        val osBean = ManagementFactory.getOperatingSystemMXBean()
        var cpuLoad = osBean.systemLoadAverage
        if (cpuLoad < 0) {
            cpuLoad = osBean.availableProcessors * ManagementFactory.getThreadMXBean().threadCount / 100.0
        }

        val tpsColor = when {
            tps >= 18.0 -> "§2"  // Dark green
            tps >= 15.0 -> "§e"  // Yellow
            else -> "§c"         // Red
        }

        return Component.text(
            "\n§7RAM: §f${usedMemory}MB / ${maxMemory}MB" +
            "\n§7CPU: §f${df.format(cpuLoad)}%" +
            "\n§7TPS: $tpsColor${df.format(tps)}"
        )
    }

    fun getServerInfo(): String {
        val info = StringBuilder()

        // Memory information
        val runtime = Runtime.getRuntime()
        val maxMemory = runtime.maxMemory() / (1024 * 1024)
        val allocatedMemory = runtime.totalMemory() / (1024 * 1024)
        val freeMemory = runtime.freeMemory() / (1024 * 1024)
        val usedMemory = allocatedMemory - freeMemory

        // CPU usage
        val osBean = ManagementFactory.getOperatingSystemMXBean()
        var cpuLoad = osBean.systemLoadAverage
        if (cpuLoad < 0) {
            cpuLoad = osBean.availableProcessors * ManagementFactory.getThreadMXBean().threadCount / 100.0
        }

        // Format server info using string templates
        info.append("§6===== SERVER INFO =====\n")
        info.append("§aRAM Usage: §f${usedMemory}MB / ${allocatedMemory}MB\n")
        info.append("§aMax RAM: §f${maxMemory}MB\n")
        info.append("§aCPU Load: §f${df.format(cpuLoad)}%\n")  // Added % sign
        info.append("§aTPS: ${formatTPS(tps)}\n")

        return info.toString()
    }

    private fun formatTPS(tps: Double): String {
        return when {
            tps >= 18.0 -> "§2${df.format(tps)}"  // Dark green
            tps >= 15.0 -> "§e${df.format(tps)}"  // Yellow
            else -> "§c${df.format(tps)}"         // Red
        }
    }

    fun getPingForPlayer(player: Player): Int {
        return try {
            // Get player ping using reflection
            val entityPlayer = player.javaClass.getMethod("getHandle").invoke(player)
            val pingField = entityPlayer.javaClass.getField("ping")
            pingField.getInt(entityPlayer)
        } catch (e: Exception) {
            logger.warning("Failed to get ping for player: ${e.message}")
            -1
        }
    }

    // Command executor inner class
    private inner class ServerInfoCommand(private val plugin: Server_info) : CommandExecutor {

        override fun onCommand(sender: CommandSender, cmd: Command, label: String, args: Array<out String>): Boolean {
            if (cmd.name.equals("serverinfo", ignoreCase = true)) {
                // Send server info
                sender.sendMessage(plugin.getServerInfo())

                // If the sender is a player, show their ping
                if (sender is Player) {
                    val ping = plugin.getPingForPlayer(sender)
                    sender.sendMessage("§aYour Ping: §f${ping}ms")
                }

                return true
            }
            return false
        }
    }
}