import dev.inmo.micro_utils.common.MPPFile
import dev.inmo.micro_utils.common.filesize
import dev.inmo.micro_utils.coroutines.runCatchingSafely
import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions
import dev.inmo.tgbotapi.bot.ktor.telegramBot
import dev.inmo.tgbotapi.bot.TelegramBot
import dev.inmo.tgbotapi.extensions.api.bot.getMe
import dev.inmo.tgbotapi.extensions.api.bot.setMyCommands
import dev.inmo.tgbotapi.extensions.api.send.*
import dev.inmo.tgbotapi.extensions.api.send.media.sendDocument
import dev.inmo.tgbotapi.extensions.api.send.media.sendDocumentsGroup
import dev.inmo.tgbotapi.extensions.behaviour_builder.buildBehaviourWithLongPolling
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onCommandWithArgs
import dev.inmo.tgbotapi.requests.abstracts.asMultipartFile
import dev.inmo.tgbotapi.types.BotCommand
import dev.inmo.tgbotapi.types.chat.Chat
import dev.inmo.tgbotapi.types.files.DocumentFile
import dev.inmo.tgbotapi.types.media.TelegramMediaDocument
import dev.inmo.tgbotapi.types.mediaCountInMediaGroup
import kotlinx.coroutines.runBlocking
import okio.FileSystem
import okio.Path.Companion.toPath

private const val command = "send_file"

/**
 * This bot will send files inside of working directory OR from directory in the second argument.
 * You may send /send_file command to this bot to get random file from the directory OR
 * `/send_file $number` when you want to receive required number of files. For example,
 * /send_file and `/send_file 1` will have the same effect - bot will send one random file.
 * But if you will send `/send_file 5` it will choose 5 random files and send them as group
 */
fun main(args: Array<String>) = runBlocking {
    val botToken = args.first()
    val directoryOrFile = args.getOrNull(1) ?.toPath() ?: "".toPath()

    fun pickFile(currentRoot: MPPFile = directoryOrFile): MPPFile? {
        if (FileSystem.SYSTEM.exists(currentRoot) && FileSystem.SYSTEM.listOrNull(currentRoot) == null) {
            return currentRoot
        } else {
            return pickFile(FileSystem.SYSTEM.list(currentRoot).takeIf { it.isNotEmpty() } ?.random() ?: return null)
        }
    }

    suspend fun TelegramBot.sendFiles(chat: Chat, files: List<MPPFile>) {
        when (files.size) {
            1 -> {
                sendDocument(
                    chat.id,
                    files.first().asMultipartFile(),
                    protectContent = true
                )
            }
            else -> sendDocumentsGroup(
                chat,
                files.map { TelegramMediaDocument(it.asMultipartFile()) },
                protectContent = true
            )
        }
    }

    val bot = telegramBot(botToken)

    bot.buildBehaviourWithLongPolling (defaultExceptionsHandler = { it.printStackTrace() }) {
        onCommandWithArgs(command) { message, args ->
            withUploadDocumentAction(message.chat) {
                val count = args.firstOrNull() ?.toIntOrNull() ?: 1
                var sent = false

                var left = count
                val chosen = mutableListOf<MPPFile>()

                while (left > 0) {
                    val picked = pickFile() ?.takeIf { it.filesize > 0 } ?: continue
                    chosen.add(picked)
                    left--
                    if (chosen.size >= mediaCountInMediaGroup.last) {
                        runCatchingSafely {
                            sendFiles(message.chat, chosen)
                        }.onFailure {
                            it.printStackTrace()
                        }
                        chosen.clear()
                        sent = true
                    }
                }

                if (chosen.isNotEmpty()) {
                    sendFiles(message.chat, chosen)
                    sent = true
                }

                if (!sent) {
                    reply(message, "Nothing selected :(")
                }
            }
        }

        setMyCommands(
            BotCommand(command, "Send some random file in picker directory")
        )

        allUpdatesFlow.subscribeSafelyWithoutExceptions(this) {
            println(it)
        }

        println(getMe())
    }.join()
}