package net.taehui.twilight.system

import net.taehui.twilight.BaseCompiler
import net.taehui.twilight.Logger
import net.taehui.twilight.TwilightComponent
import net.taehui.twilight.Utility
import org.jline.reader.UserInterruptException
import java.nio.file.Files
import java.nio.file.Path
import java.time.LocalDateTime
import java.util.concurrent.CompletableFuture
import java.util.concurrent.atomic.AtomicInteger
import java.util.stream.StreamSupport


object IO : Logger {
    private val futureLearnNotesStatus = AtomicInteger()
    private var futureLearnNotes: CompletableFuture<Void>? = null
    private var futureWipeNotes: Collection<String>? = null
    private var futurePMSNotes: Collection<String>? = null
    private var futureWipeComments: Collection<Path>? = null
    private var futureWipeBundles: Collection<Path>? = null

    fun handleSystem(): String {
        var stopText: String? = null
        val r = Window.window.r
        while (stopText == null) {
            try {
                val line = r.readLine("> ")
                if (line.isNotEmpty()) {
                    val w = r.parsedLine.words()
                    if (futureWipeNotes != null) {
                        if ("y".equals(w[0], true)) {
                            futureWipeNotes?.forEach {
                                DB.wipeNote(it, this)
                            }
                        } else {
                            logInfo("Cancelled")
                        }
                        futureWipeNotes = null
                        continue
                    }
                    if (futurePMSNotes != null) {
                        if ("y".equals(w[0], true)) {
                            futurePMSNotes?.forEach {
                                DB.pmsNote(it, this)
                            }
                        } else {
                            logInfo("Cancelled")
                        }
                        futurePMSNotes = null
                        continue
                    }
                    if (futureWipeComments != null) {
                        if ("y".equals(w[0], true)) {
                            DB.wipeComments(futureWipeComments!!).thenAccept {
                                logInfo("Wiped $it Comment Files")
                            }
                        } else {
                            logInfo("Cancelled")
                        }
                        futureWipeComments = null
                        continue
                    }
                    if (futureWipeBundles != null) {
                        if ("y".equals(w[0], true)) {
                            DB.wipeBundles(futureWipeBundles!!).thenAccept {
                                logInfo("Wiped $it Bundle Files")
                            }
                        } else {
                            logInfo("Cancelled")
                        }
                        futureWipeBundles = null
                        continue
                    }
                    when (w[0]) {
                        "stop" -> stopText = if (w.size > 1) w[1] else ""

                        "alarm" -> {
                            if (w.size > 2) {
                                SiteHandler.setSiteNotify(
                                    SiteHandler.getSiteID(w[1]), w[2]
                                )
                            } else if (w.size > 1) {
                                AvatarHandler.justNotify(w[1])
                            } else {
                                logInfo("alarm <Site ID> <Text>")
                            }
                        }

                        "avatar" -> {
                            if (w.size > 1) {
                                when (w[1]) {
                                    "view" -> {
                                        AvatarHandler.doInfo(this)
                                        continue
                                    }

                                    "quit" -> {
                                        if (w.size > 2) {
                                            AvatarHandler.handleNotSignIn(w[2])
                                        } else {
                                            logInfo("avatar quit <Avatar ID>")
                                        }
                                        continue
                                    }

                                    "ban" -> {
                                        if (w.size > 3) {
                                            AvatarHandler.getAvatar(
                                                w[2]
                                            ).let {
                                                if (it != null) {
                                                    BannedIP.putBannedIP(
                                                        it.remote, LocalDateTime.parse(
                                                            w[3]
                                                        )
                                                    )
                                                    it.close()
                                                } else {
                                                    logInfo("Failed to Find ID")
                                                }
                                            }
                                        } else {
                                            logInfo("avatar ban <Avatar ID> <Date>")
                                        }
                                        continue
                                    }

                                    "allow" -> {
                                        if (w.size > 2) {
                                            BannedIP.wipeBannedIP(w[2])
                                        } else {
                                            logInfo("avatar allow <Avatar IP>")
                                        }
                                        continue
                                    }
                                }
                            }
                            logInfo("avatar [view] View Avatars")
                            logInfo("avatar [quit] Quit Avatar")
                            logInfo("avatar [ban] Ban Avatar IP")
                            logInfo("avatar [allow] Allow Avatar IP")
                        }

                        "awilight" -> {
                            if (w.size > 2) {
                                when (w[1]) {
                                    "set" -> {
                                        if (w.size > 3) {
                                            val awilightCount = w[2].toInt()
                                            Configure.awilightCount =
                                                0.coerceAtLeast(Configure.awilightCount + awilightCount)
                                            AwilightHandler.setAwilightCount(
                                                awilightCount
                                            )
                                        } else {
                                            logInfo("awilight set <Count>")
                                        }
                                        continue
                                    }
                                }
                            }
                            logInfo("awilight [set] Set Awilight")
                        }

                        "bundle" -> {
                            if (w.size > 1) {
                                when (w[1]) {
                                    "wipe" -> {
                                        DB.getWipeBundles().thenAccept {
                                            if (it.isNotEmpty()) {
                                                futureWipeBundles = it
                                            }
                                            logInfo("${it.size} Bundle Files")
                                        }
                                        continue
                                    }
                                }
                            }
                            logInfo("bundle [wipe] Clean Bundle Files")
                        }

                        "comment" -> {
                            if (w.size > 1) {
                                when (w[1]) {
                                    "wipe" -> {
                                        DB.getWipeComments().thenAccept {
                                            if (it.isNotEmpty()) {
                                                futureWipeComments = it
                                            }
                                            logInfo("${it.size} Comment Files")
                                        }
                                        continue
                                    }
                                }
                            }
                            logInfo("comment [wipe] Clean Comment Files")
                        }

                        "note" -> {
                            if (w.size > 1) {
                                when (w[1]) {
                                    "learn" -> {
                                        fun learnNoteFile(noteFilePath: Path): Boolean {
                                            return try {
                                                val noteFileData = Files.readAllBytes(noteFilePath)
                                                BaseCompiler.handleCompile(noteFileData, -1).forEach {
                                                    DB.setNote(
                                                        it.noteID,
                                                        Utility.getID128(noteFileData),
                                                        Utility.getID256(noteFileData),
                                                        it
                                                    )
                                                }
                                                true
                                            } catch (e: Throwable) {
                                                logFault(e)
                                                false
                                            }
                                        }

                                        if (w.size > 2) {
                                            if (learnNoteFile(Path.of(w[2]))) {
                                                logInfo("Learned Note File")
                                                NoteFilesSystem.loadNoteFiles()
                                            }
                                        } else {
                                            if (futureLearnNotes?.isDone != false) {
                                                futureLearnNotes = logFuture {
                                                    DB.wipeNotes()
                                                    try {
                                                        Files.newDirectoryStream(
                                                            TwilightComponent.NOTE_ENTRY_PATH
                                                        ).use {
                                                            StreamSupport.stream(it.spliterator(), true)
                                                                .forEach { noteFilePath ->
                                                                    if (learnNoteFile(noteFilePath)) {
                                                                        futureLearnNotesStatus.incrementAndGet()
                                                                    }
                                                                }
                                                        }
                                                    } finally {
                                                        logInfo(
                                                            "Learned ${futureLearnNotesStatus.get()} Note Files"
                                                        )
                                                        futureLearnNotesStatus.set(0)
                                                        NoteFilesSystem.loadNoteFiles()
                                                    }
                                                }
                                            } else {
                                                logInfo(
                                                    "Learning ${futureLearnNotesStatus.get()} Note Files"
                                                )
                                            }
                                        }
                                        continue
                                    }

                                    "ban" -> {
                                        if (w.size > 3) {
                                            BannedNoteFile.putBannedNoteFile(w[2], w[3])
                                        } else {
                                            logInfo("note ban <Note ID> <Comment>")
                                        }
                                        continue
                                    }

                                    "allow" -> {
                                        if (w.size > 2) {
                                            BannedNoteFile.wipeBannedNoteFile(w[2])
                                        } else {
                                            logInfo("note allow <Note ID>")
                                        }
                                        continue
                                    }

                                    "wipe" -> {
                                        if (w.size > 2) {
                                            DB.wipeNote(w[2], this)
                                        } else {
                                            DB.getWipeNotes().thenAccept {
                                                if (it.isNotEmpty()) {
                                                    futureWipeNotes = it
                                                }
                                                logInfo("${it.size} Notes")
                                            }
                                        }
                                        continue
                                    }

                                    "pms" -> {
                                        if (w.size > 2) {
                                            DB.pmsNote(w[2], this)
                                        } else {
                                            DB.getPMSNotes().thenAccept {
                                                if (it.isNotEmpty()) {
                                                    futurePMSNotes = it
                                                }
                                                logInfo("${it.size} Notes")
                                            }
                                        }
                                        continue
                                    }
                                }
                            }
                            logInfo("note [learn] <Path> Learn Notes")
                            logInfo("note [ban] <Note ID> Ban Note")
                            logInfo("note [allow] <Note ID> Allow Note")
                            logInfo("note [wipe] <Note ID> Clean or Wipe Note File")
                            logInfo("note [pms] <Note ID> PMS Note File")
                        }

                        "pause" -> {
                            Configure.mode.pause = !Configure.mode.pause
                            if (w.size > 1) {
                                AvatarHandler.pause(w[1])
                            } else {
                                AvatarHandler.pause("")
                            }
                        }

                        "platform" -> {
                            if (w.size > 1) {
                                when (w[1]) {
                                    "avatar" -> {
                                        if (w.size > 3) {
                                            PlatformIDSystem.putPlatformID(w[2], w[3])
                                        } else if (w.size > 2) {
                                            PlatformIDSystem.wipePlatformID(w[2])
                                        } else {
                                            logInfo("platform avatar <Platform ID> <Qwilight ID>")
                                        }
                                        continue
                                    }

                                    "view" -> {
                                        logInfo(PlatformSystem.platformStatus)
                                        continue
                                    }

                                    "dispose" -> {
                                        PlatformSystem.dispose()
                                        continue
                                    }

                                    "handle" -> {
                                        PlatformSystem.handleSystem()
                                        continue
                                    }
                                }
                            }
                            logInfo("platform [avatar] Handle Avatars")
                            logInfo("platform [view] View Platform")
                            logInfo("platform [dispose] Close Platform")
                            logInfo("platform [handle] Handle Platform")
                        }

                        "ram" -> {
                            Runtime.getRuntime().gc()
                            logInfo(
                                "Heap: ${
                                    Runtime.getRuntime().totalMemory() / 1000 / 1000
                                } MB / ${Runtime.getRuntime().maxMemory() / 1000 / 1000} MB"
                            )
                        }

                        "site" -> {
                            if (w.size > 1) {
                                when (w[1]) {
                                    "view" -> {
                                        SiteHandler.doInfo(this)
                                        continue
                                    }

                                    "put" -> {
                                        if (w.size > 2) {
                                            SiteHandler.putSite(w[2])
                                        } else {
                                            logInfo("site put <Name>")
                                        }
                                        continue
                                    }

                                    "wipe" -> {
                                        if (w.size > 2) {
                                            SiteHandler.wipeSite(SiteHandler.getSiteID(w[2]))
                                        } else {
                                            logInfo("site wipe <Site ID>")
                                        }
                                        continue
                                    }

                                    "silent" -> {
                                        if (w.size > 2) {
                                            SiteHandler.silentSite(SiteHandler.getSiteID(w[2]))
                                        } else {
                                            logInfo("site silent <Site ID>")
                                        }
                                        continue
                                    }
                                }
                            }
                            logInfo("site [view] View Sites")
                            logInfo("site [put] Put Site")
                            logInfo("site [wipe] Wipe Site")
                            logInfo("site [silent] Silent Site")
                        }

                        "tv" -> {
                            if (w.size > 1) {
                                when (w[1]) {
                                    "view" -> {
                                        logInfo(TVSystem.tvStatus)
                                        continue
                                    }

                                    "dispose" -> {
                                        TVSystem.dispose()
                                        continue
                                    }

                                    "handle" -> {
                                        TVSystem.handleSystem()
                                        continue
                                    }
                                }
                            }
                            logInfo("tv [view] View TV")
                            logInfo("tv [dispose] Close TV")
                            logInfo("tv [handle] Handle TV")
                        }

                        else -> {
                            logInfo("[alarm] Send Notify")
                            logInfo("[avatar] Handle Avatars")
                            logInfo("[awilight] Handle Awilight")
                            logInfo("[bundle] Handle Bundles")
                            logInfo("[comment] Handle Comments")
                            logInfo("[note] Handle Notes")
                            logInfo("[pause] Pause Twilight")
                            logInfo("[platform] Handle Platform")
                            logInfo("[site] Handle Sites")
                            logInfo("[tv] Handle TV")
                            logInfo("[stop] Stop Twilight")
                        }
                    }
                }
            } catch (_: UserInterruptException) {
            } catch (e: Throwable) {
                logFault(e)
            }
        }
        return stopText
    }
}