package com.twm.tools.local

import java.io.File
import java.io.RandomAccessFile
import java.util.Scanner
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors
import java.util.concurrent.Future
import java.util.concurrent.Semaphore
import java.util.concurrent.TimeUnit
import javax.xml.parsers.SAXParserFactory
import javax.xml.transform.OutputKeys
import javax.xml.transform.sax.SAXTransformerFactory
import javax.xml.transform.stream.StreamResult
import kotlin.system.exitProcess


object MainClass {
    private const val TARGET_LIB = "libBase"
    private const val STRING_FILE = "strings.xml"
    private const val LIMITED_QPS = 6
    private val translateApi = BaiduTranslateService
    private val semaphore = Semaphore(LIMITED_QPS)
    private lateinit var taskExecutor: ExecutorService

    /**
     * 每次新增多语言, 在默认strings.xml处新增完毕后，点此处开始即可同步所有多语言
     */
    @JvmStatic
    fun main(args: Array<String>) {
        prepareHttpExecutors()
        syncTranslations(listOf(
            AndroidLanguage.EN,
            AndroidLanguage.DE,
            AndroidLanguage.JP,
            AndroidLanguage.KO,
            AndroidLanguage.HK,
            AndroidLanguage.RU,
            AndroidLanguage.FR,
            AndroidLanguage.TH,
        ))
        exitProcess(0)
    }

    private fun prepareHttpExecutors() {
        semaphore.acquire(LIMITED_QPS)
        val executor = Executors.newScheduledThreadPool(1)
        val qpsRelease = {
            semaphore.release(1)
        }
        executor.scheduleAtFixedRate(
            qpsRelease, 0, (1000f / LIMITED_QPS).toLong(), TimeUnit.MILLISECONDS
        )
        taskExecutor = Executors.newFixedThreadPool(LIMITED_QPS)
    }

    private fun syncTranslations(languages: List<AndroidLanguage>) {
        val parentDir = File("${TARGET_LIB}/src/main/res")
        val templateDir = File(parentDir, "values")
        if (!templateDir.exists()) {
            throw IllegalStateException("没有找到${TARGET_LIB}下的values文件夹")
        }
        val templateFile = File(templateDir, STRING_FILE)
        if (!templateFile.exists()) {
            throw IllegalStateException("模板values中无strings.xml文件")
        }
        for (language in languages) {
            val langDir = File(parentDir, "values-${language.valuesLang}")
            if (!langDir.exists() && !langDir.mkdirs()) {
                throw IllegalStateException("创建文件夹失败:${langDir}")
            }
            val langFile = File(langDir, STRING_FILE)
            if (!langFile.exists() && !langFile.createNewFile()) {
                throw IllegalStateException("创建文件失败:${langFile}")
            }
        }

        // parse languages
        val saxFactory = SAXParserFactory.newInstance()
        val saxParser = saxFactory.newSAXParser()
        val saxHandler = ValuesStringSaxHandler()
        val saxResult = runCatching {
            saxParser.parse(templateFile, saxHandler)
        }
        if (saxResult.isFailure) {
            println("模板strings文件解析失败:${saxResult.exceptionOrNull()}")
            return
        }
        val templateValues = saxHandler.getCollectValues()
        val holdingValues = mutableMapOf<AndroidLanguage, Set<String>>()
        for (language in languages) {
            val stringFile = getStringValuesDir(language)
            val valueHandler = ValuesStringSaxHandler()
            val parseResult = runCatching {
                saxParser.parse(stringFile, valueHandler)
            }
            if (parseResult.isFailure) {
                println("${language}中的文件内容解析失败或内容为空")
            }
            holdingValues[language] = valueHandler
                .getCollectValues()
                .map { it.key }
                .toHashSet()
        }
        val pendingValues = mutableMapOf<AndroidLanguage, MutableList<Translation>>()
        for (value in templateValues) {
            for (entry in holdingValues.entries) {
                if (entry.value.contains(value.key)) {
                    continue
                }
                val valueList = pendingValues.getOrPut(entry.key) {
                    mutableListOf()
                }
                valueList.add(Translation(ValuesData(value.key), value.value))
                println("${entry.key.valuesLang} 待翻译key => ${value.key}")
            }
        }
        if (pendingValues.isEmpty()) {
            println("所有翻译均与默认strings.xml同步, 无新增翻译")
            return
        }
        println("<==========================================>")
        for (entry in pendingValues.entries) {
            if (entry.value.isEmpty()) {
                println("${entry.key.valuesLang} 无新增翻译内容")
                continue
            }
            inflateTranslations(entry.key, entry.value, taskExecutor)
            writeResultToFile(entry.key, entry.value)
            println("写入文件完成:${entry.key.valuesLang}, 翻译数量:${entry.value.size}")
            println("<==========================================>")
        }
    }

    private fun inflateTranslations(
        language: AndroidLanguage,
        translations: List<Translation>,
        executor: ExecutorService
    ) {
        val records = mutableMapOf<ValuesData, Future<String?>>()
        for (translation in translations) {
            val callable = TranslateCallable(
                translateApi, translation.template, language.baiduLang
            )
            val qpsTask = QPSTaskWrapper(semaphore, callable)
            records[translation.valueData] = executor.submit(qpsTask)
        }
        for (record in records) {
            try {
                val translation = record.value.get()
                if (translation == null) {
                    println("翻译失败 => ${language.valuesLang} => ${record.key.key}")
                } else {
                    record.key.value = processForTranslation(language, translation)
                    println("翻译完成 => ${language.valuesLang} => ${record.key}")
                }
            } catch (ex: Exception) {
                ex.printStackTrace()
            }
        }
    }

    private fun writeResultToFile(
        language: AndroidLanguage,
        translations: List<Translation>
    ) {
        val targetFile = getStringValuesDir(language)
        ensureValidXmlFile(targetFile)
        val accessFile = RandomAccessFile(targetFile, "rw")
        val fileLength = accessFile.length()
        val endTag = "</resources>"
        if (fileLength <= endTag.length * 2) {
            throw RuntimeException("strings.xml is invalid!")
        }
        var seekPosition = endTag.length
        var resourceEnd = -1
        while (seekPosition <= fileLength) {
            accessFile.seek(fileLength - seekPosition)
            val currentLine = accessFile.readLine()
            if (currentLine.contains("</resources>")) {
                resourceEnd = seekPosition
                break
            }
            seekPosition++
        }
        val current = resourceEnd
        if (current < 0) {
            throw IllegalStateException("strings.xml文件格式有误")
        }
        accessFile.seek(fileLength - current)
        for (translation in translations) {
            val insertString = "    <string name=\"${translation.valueData.key}\">${translation.valueData.value}</string>"
            accessFile.write(insertString.toByteArray())
            accessFile.writeBytes("\n")
        }
        accessFile.writeBytes(endTag)
        accessFile.close()
    }

    private fun ensureValidXmlFile(file: File) {
        var validFile = false
        val scanner = Scanner(file)
        while (scanner.hasNextLine()) {
            val nextLine = scanner.nextLine()
            if (nextLine.contains("<resources>")) {
                validFile = true
            }
        }
        if (validFile) {
            return
        }
        val target = SAXTransformerFactory.newInstance() as SAXTransformerFactory
        val handler = target.newTransformerHandler()
        handler.transformer.apply {
            setOutputProperty(OutputKeys.ENCODING, "UTF-8")
            setOutputProperty(OutputKeys.INDENT, "yes")
        }
        val result = StreamResult(file)
        handler.setResult(result)
        handler.startDocument()
        val indent = "\n".toCharArray()
        handler.characters(indent, 0, indent.size)
        handler.startElement("", "", "resources", null)
        handler.characters(indent, 0, indent.size)
        handler.endElement("", "", "resources")
        handler.endDocument()
    }

    private fun processForTranslation(language: AndroidLanguage, source: String): String {
        if (language != AndroidLanguage.FR) {
            return source
        }
        return source.replace("'", "\\'")
    }

    private fun getStringValuesDir(language: AndroidLanguage): File {
        return File("${TARGET_LIB}/src/main/res/values-${language.valuesLang}/${STRING_FILE}")
    }
}