package codes

import content.ScheduleDay
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import oshi.SystemInfo
import oshi.hardware.CentralProcessor
import oshi.hardware.GlobalMemory
import java.nio.file.Files
import java.nio.file.Path
import java.time.format.DateTimeFormatter
import java.util.function.Consumer
import kotlin.io.path.name


@Suppress("unused")
class Somecodes {
    companion object {
        val current_directory : String = System.getProperty("user.dir")
        val si = SystemInfo()
        val processor: CentralProcessor = si.hardware.processor
        val memory : GlobalMemory = si.hardware.memory
        val datetimeformat1: DateTimeFormatter = DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm:ss")
        val dateformat1: DateTimeFormatter = DateTimeFormatter.ofPattern("dd/MM/yyyy")
        val timeformat1: DateTimeFormatter = DateTimeFormatter.ofPattern("hh:mm:ss")
        val timeformat2: DateTimeFormatter = DateTimeFormatter.ofPattern("hh:mm")
        const val KB_threshold = 1024.0
        const val MB_threshold = KB_threshold * 1024.0
        const val GB_threshold = MB_threshold * 1024.0
        const val TB_threshold = GB_threshold * 1024.0

        /**
         * List all audio files (.mp3 and .wav) in the specified directory and its subdirectories.
         * Only files larger than 1KB are included.
         * @param dir The directory to search in (default is the current working directory).
         * @return A list of absolute paths to the audio files found.
         */
        fun ListAudioFiles(dir: String = current_directory) : List<String>{
            return try{
                // find all files that ends with .mp3 or .wav
                // and find them recursively
                val p = Path.of(dir)
                if (Files.exists(p) && Files.isDirectory(p)){
                    Files.walk(p)
                        // cari file regular saja
                        .filter { Files.isRegularFile(it)}
                        // size lebih dari 1KB
                        .filter { Files.size(it) > 1024}
                        // extension .mp3 atau .wav
                        .filter { it.name.endsWith(".mp3",true) || it.name.endsWith(".wav",true) }
                        .map { it.toAbsolutePath().toString() }
                        .toList()
                } else throw Exception()

            } catch (_ : Exception){
                emptyList()
            }
        }

        /**
         * Converts a size in bytes to a human-readable format.
         * @param size Size in bytes.
         * @return A string representing the size in a human-readable format.
         */
        fun SizetoHuman(size: Long): String {
            return when {
                size < KB_threshold -> "${size}B"
                size < MB_threshold -> String.format("%.2f KB", size / KB_threshold)
                size < GB_threshold -> String.format("%.2f MB", size / MB_threshold)
                size < TB_threshold -> String.format("%.2f GB", size / GB_threshold)
                else -> String.format("%.2f TB", size / TB_threshold)
            }
        }

        /**
         * Get Disk usage using OSHI library.
         * @param path The path to check disk usage, defaults to the current working directory.
         * @return A string representing the disk usage of the file system in a human-readable format.
         */
        fun getDiskUsage(path: String = current_directory) : String {
            return try{
                val p = Path.of(path).toFile()
                if (p.exists() && p.isDirectory){
                    val total = p.totalSpace
                    val free = p.freeSpace
                    val used = total - free
                    String.format("Total: %s, Used: %s, Free: %s, Usage: %.2f%%",
                        SizetoHuman(total),
                        SizetoHuman(used),
                        SizetoHuman(free),
                        (used.toDouble() / total * 100)
                    )
                } else throw Exception()

            } catch (_ : Exception){
                "N/A"
            }
        }

        /**
         * Get CPU usage using OSHI library.
         * @param cb A callback function that receives the CPU usage as a string.
         */
        fun getCPUUsage(cb : Consumer<String>){
            CoroutineScope(Dispatchers.Default).launch {
                val prev = processor.systemCpuLoadTicks
                delay(1000)
                val current = processor.systemCpuLoadTicks

                fun delta(t: CentralProcessor.TickType) = current[t.index] - prev[t.index]

                val idle = delta(CentralProcessor.TickType.IDLE) + delta(CentralProcessor.TickType.IOWAIT)
                val busy =  delta(CentralProcessor.TickType.USER) + delta(CentralProcessor.TickType.SYSTEM) +
                            delta(CentralProcessor.TickType.NICE) + delta(CentralProcessor.TickType.IRQ) +
                            delta(CentralProcessor.TickType.SOFTIRQ)+ delta(CentralProcessor.TickType.STEAL)

                val total = idle + busy
                val usage = if (total > 0) {
                    (busy.toDouble() / total) * 100
                } else {
                    0.0
                }
                cb.accept(String.format("%.2f%%", usage))
            }

        }

        /**
         * Get RAM usage using OSHI library.
         * @return A string representing the total, used, and available memory in a human-readable format.
         */
        fun getMemoryUsage() : String{
            val totalMemory = memory.total
            val availableMemory = memory.available
            val usedMemory = totalMemory - availableMemory
            return String.format("Total: %s, Used: %s, Available: %s, Usage: %.2f%%",
                SizetoHuman(totalMemory),
                SizetoHuman(usedMemory),
                SizetoHuman(availableMemory)
            , (usedMemory.toDouble() / totalMemory * 100))
        }

        /**
         * Check if a value is a valid non-blank string.
         * @param value The value to check.
         * @return True if the value is a non-blank string, false otherwise.
         */
        fun ValidString(value: Any) : Boolean {
            return value is String && value.isNotBlank()
        }

        /**
         * Check if a string is a valid file path and the file exists.
         * @param value The string to check.
         * @return True if the string is a valid file path, false otherwise.
         */
        fun ValidFile(value : String) : Boolean {
            if (value.isNotBlank()){
                return Files.exists(Path.of(value))
            }
            return false
        }

        /**
         * Check if a string is a valid date in the format "dd/MM/yyyy".
         * @param value The string to check.
         * @return True if the string is a valid date, false otherwise.
         */
        fun ValidDate(value: String): Boolean{
            return try{
                if (ValidString(value)){
                    dateformat1.parse(value)
                    true
                } else throw Exception()

            } catch (_: Exception){
                false
            }
        }

        /**
         * Check if a string is a valid time in the format "hh:mm:ss".
         * @param value The string to check.
         * @return True if the string is a valid time, false otherwise.
         */
        fun ValidTime(value: String): Boolean{
            return try{
                if (ValidString(value)){
                    timeformat1.parse(value)
                    true
                } else throw Exception()

            } catch (_: Exception){
                false
            }
        }

        /**
         * Check if a string is a valid schedule time in the format "HH:mm".
         * @param value The string to check.
         * @return True if the string is a valid schedule time, false otherwise.
         */
        fun ValidScheduleTime(value: String): Boolean{
            // format HH:mm
            try {
                if (ValidString(value)){
                    timeformat2.parse(value)
                    return true
                }
            } catch (_ : Exception){

            }
            return false
        }

        /**
         * Find a schedule day by its name.
         * @param value The name of the schedule day to find.
         * @return The name of the schedule day if found, null otherwise.
         */
        fun FindScheduleDay(value: String) : String? {
            val sd = ScheduleDay.entries.find { sd -> sd.name == value }
            return sd?.name
        }

        /**
         * Check if a string is a valid schedule day or a valid date.
         * A valid schedule day is either one of the ScheduleDay enum names or a date in the format "dd/MM/yyyy".
         * @param value The string to check.
         * @return True if the string is a valid schedule day or date, false otherwise.
         */
        fun ValidScheduleDay(value: String) : Boolean {
            if (ValidString(value)){
                // check if value is one of ScheduleDay enum name
                if (FindScheduleDay(value) != null){
                    return true
                }
                // check if value is in format dd/MM/yyyy
                return ValidDate(value)
            }
            return false
        }
    }


}