package kr.lunaticbum.back.lun.controllers

import bums.lunatic.launcher.utils.CompressStringUtil
import com.google.gson.Gson
import com.google.gson.annotations.SerializedName
import com.google.maps.GeoApiContext
import com.google.maps.PlacesApi
import com.google.maps.model.LatLng
import com.google.maps.model.PlaceType
import com.google.maps.model.PlacesSearchResult
import com.google.maps.model.RankBy
import jakarta.servlet.http.HttpServletRequest
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.launch
import kr.lunaticbum.back.lun.configs.GlobalEnvironment
import kr.lunaticbum.back.lun.model.*
import kr.lunaticbum.back.lun.service.Lama
import kr.lunaticbum.back.lun.utils.LogService
import kr.lunaticbum.back.lun.utils.extractModelData
import org.springframework.ai.chat.messages.UserMessage
import org.springframework.ai.chat.prompt.Prompt
import org.springframework.ai.ollama.api.OllamaApi
import org.springframework.ai.ollama.api.OllamaOptions
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.beans.factory.annotation.Qualifier
import org.springframework.context.annotation.Bean
import org.springframework.http.MediaType
import org.springframework.scheduling.annotation.Scheduled
import org.springframework.ui.ModelMap
import org.springframework.web.bind.annotation.*
import org.springframework.web.reactive.function.BodyInserters
import org.springframework.web.reactive.function.client.WebClient
import reactor.core.publisher.Mono
import java.math.BigDecimal
import java.math.RoundingMode
import java.text.SimpleDateFormat
import java.time.Duration
import java.util.*
import java.util.prefs.Preferences


@RestController
@RequestMapping("/tlg")
class Telegram {


    @Autowired
    lateinit var globalEvv : GlobalEnvironment

    @Autowired
    lateinit var telegramService: TelegramMsgService
    @Autowired
    lateinit var logService: LogService

    @Autowired
    lateinit var locationLogService: LocationLogService

    @ResponseBody
    @GetMapping("hello")
    fun hello(): String {
        return "hello1212"
    }

    @Autowired
    lateinit var rssDataService: RssDataService



    val keyworkd = arrayListOf("I0Z","dcBEW", "TGyG", "U=Qu", "Bm=s")
    val keyworkd2 = arrayListOf("x-n", "Y_D", "u", "uoo", "dfhZ", "gSKY")

    @ResponseBody
    @PostMapping("repotToMe.bjx")
    fun repotToMe(@RequestBody jsonString: String) {
        jsonString.extractModelData { exception, originDataString ->
            if (exception == null) {
                Gson().fromJson<ReportModel>(originDataString, ReportModel::class.java)?.let { msg ->
                    sendMsg("${msg.name}님이 전송\n${msg.message}\n회신가능 메일${msg.email}")
                }
            }
        }
    }

    @ResponseBody
    @GetMapping("kesy/{path}")
    fun getEncode(@PathVariable path: String): ModelMap {
        var returnModelMap = ModelMap()
        var comp = decodeCompressedString(path)
        returnModelMap.put("C",comp)
        returnModelMap.put("D", trimWithDecompString(comp))
        return returnModelMap
    }

    fun decodeCompressedString(value : String) : String {
        var comp = CompressStringUtil.compressString(value)
        println("comp >>> $comp")
        var chunked = Math.abs(Random().nextInt() % 3) + 1
        chunked = if (chunked % 2 == 1) chunked + 1 else chunked
        comp = comp?.chunked(chunked) {
            return@chunked it.padStart(chunked,'=')
        }?.joinToString("")?.reversed().plus("$chunked").plus(Char(Math.abs(Random().nextInt() % 57) + 65))
        var word = if (System.currentTimeMillis() % 2L == 0L) {
            keyworkd.get(chunked)
        } else  {
            comp = comp.plus(Char(Math.abs(Random().nextInt() % 57) + 65))
            keyworkd2.get(chunked)
        }
        comp =  (word).plus(comp)
        return comp
    }

    fun trimWithDecompString(comp : String) : String {
        var doubleIpmt = false
        var compressed : String? = comp
        keyworkd2.forEach { if(compressed?.startsWith(it) == true) {
            doubleIpmt = true
        } }
        var charChunked = compressed?.lastOrNull()
        println("comp?.removeSuffix(charChunked!!.toString()) ${compressed?.removeSuffix(charChunked!!.toString())}")
        compressed = compressed?.removeSuffix(charChunked!!.toString())
        if (doubleIpmt) {
            charChunked = compressed?.lastOrNull()
            compressed = compressed?.removeSuffix(charChunked!!.toString())
        }
        charChunked = compressed?.lastOrNull()
        println("charChunked >> $charChunked")
        var chunked  = charChunked?.toString()?.toInt() ?: 0
        println("chunked >> $chunked")
        println("comp?.removeSuffix(charChunked!!.toString()) ${compressed?.removeSuffix(charChunked!!.toString())}")

        compressed = (compressed?.substring(0,compressed.length -1))
        println("comp $compressed")

        compressed = compressed?.removePrefix(keyworkd.get(chunked))?.removePrefix(keyworkd2.get(chunked))?.reversed()
        println("comp $compressed")
        compressed = compressed?.chunked(chunked){
            return@chunked  it.toString().replace("=","")
        }?.joinToString("")
        println("comp $compressed")
        var decomp = CompressStringUtil.decompressString(compressed)
        println("decomp $decomp")
        return decomp
    }

    @ResponseBody
    @PostMapping("webhook")
    suspend fun test(httpServletRequest: HttpServletRequest, @RequestBody  update : kr.lunaticbum.back.lun.model.Result?, @RequestBody  updates : kr.lunaticbum.back.lun.model.TelegramUpdate? ) : String {
        try {
            println("test strat ${Gson().toJson(updates)}")
            println("test strat ${Gson().toJson(update)}")
//            println("test strat ${httpServletRequest.requestURI}")
            update?.message?.let { msg ->
                if(msg?.location != null && msg?.location?.latitude != 0.0  && msg?.location?.latitude != 0.0 ) {
                    CoroutineScope(Dispatchers.IO).launch {
                        try {
                            var pref = Preferences.userNodeForPackage(Telegram::class.java)
                            var prefKey = pref.get("GAPI_KEY".plus("_").plus(msg.from!!.id.toString()),"")
                            if (prefKey?.length ?: 0 < 4) {
                                prefKey = globalEvv.gapiKey
                            }
                            println("prefKey >> ${prefKey}")
                            if (prefKey != null && prefKey.length > 0) {
                                println("test strat ${msg.location}")
                                println("test prefKey ${prefKey}")
                                val lat = BigDecimal(msg?.location?.latitude!!).setScale(6, RoundingMode.HALF_UP)
                                val long = BigDecimal(msg?.location?.longitude!!).setScale(6, RoundingMode.HALF_UP)
                                WebClient.create().get()
                                    .uri("http://api.weatherapi.com/v1/current.json?key=${globalEvv.weatherApiKey}&q=${lat},${long}&aqi=no")
                                    .retrieve()
                                    .bodyToMono(CurrentWeather::class.java)
                                    .timeout(Duration.ofSeconds(30L))
                                    .block()?.let { sss ->
                                        println("test strat ${sss}")
                                        CoroutineScope(Dispatchers.IO).launch {
                                            try {
                                                val msg = TelegramSendMsg(
                                                    "${msg.from!!.id!!}",
                                                    sss.getSummaryInfo(lat.toString(), long.toString())
                                                )
                                                val fullUrl =
                                                    "https://api.telegram.org/${globalEvv.telegramBotKey}/sendMessage"
                                                val result = WebClient.create(fullUrl)
                                                    .post()
                                                    .contentType(MediaType.APPLICATION_JSON)
                                                    .body(BodyInserters.fromValue(Gson().toJson(msg)))
                                                    .retrieve()

                                                    .bodyToMono(String::class.java).block() ?: "FAIL"
                                                println("fullUrl ${fullUrl} : result $result")

                                            } catch (e: Exception) {
                                                e.printStackTrace()
                                            }
                                        }
                                    }

                                val context = GeoApiContext.Builder()
                                    .apiKey(prefKey.trim())
                                    .build()
                                var types =
                                    arrayOf(PlaceType.RESTAURANT, PlaceType.CAFE, PlaceType.BAR, PlaceType.BAKERY)
                                types.forEach { type ->
                                    PlacesApi.nearbySearchQuery(context, LatLng(lat.toDouble(), long.toDouble()))
                                        .type(type).rankby(RankBy.DISTANCE).language("ko").await()?.let { respoce ->
                                            respoce.results.filter {
                                                return@filter it.rating > 4 && it.userRatingsTotal > 1
                                            }.sortedBy { it.userRatingsTotal }.forEach {
                                                try {
                                                    val msg = TelegramSendMsg(
                                                        "${msg.from!!.id!!}",
                                                        "${type.name} :: " + it.summary(lat.toDouble(), long.toDouble())
                                                    )
                                                    println("msg >>> ${Gson().toJson(msg)}")
                                                    val fullUrl =
                                                        "https://api.telegram.org/${globalEvv.telegramBotKey}/sendMessage"
                                                    val result = WebClient.create(fullUrl)
                                                        .post()
                                                        .contentType(MediaType.APPLICATION_JSON)
                                                        .body(BodyInserters.fromValue(Gson().toJson(msg)))
                                                        .retrieve()
                                                        .bodyToMono(String::class.java).block() ?: "FAIL"
                                                    println("fullUrl ${fullUrl} : result $result")
                                                } catch (e: Exception) {
                                                    e.printStackTrace()
                                                }
                                            }
                                        }
                                }
                            }
                            else {
                                sendSimpleMsg(globalEvv.telegramBotKey!!,msg.from!!.id.toString(),"서비스 키를 등록하셈.\n/setGaipKeys {key}")
                            }
                        }
                        catch(e : Exception) {
                            e.printStackTrace()
                        }
                    }
                } else if(msg.text?.startsWith("/") == true) {
//                    msg.text?.split(" ")?.let { cmds ->
//                        cmds[0].let { cmd ->
//                            when(cmd.trim()) {
//                                "/reqGapiKeys" -> {
//                                    sendSimpleMsg(globalEvv.telegramBotKey!!,globalEvv.telegramMyId!!,"${msg.from!!.id.toString()}님이 서비스 키를 요첨항./setGaipKeys {key}")
//                                }
//                                "/setGaipKeys" -> {
//                                    var pref = Preferences.userNodeForPackage(Telegram::class.java)
//                                    pref.put("GAPI_KEY".plus("_").plus(msg.from!!.id.toString()), cmds[1])
//                                    pref.sync()
//                                    println("test prefKey ${"GAPI_KEY".plus("_").plus(msg.from!!.id.toString())}")
//                                    println("test prefKey ${cmds[1]}")
//                                    println("test prefKey ${pref.get("GAPI_KEY".plus("_").plus(msg.from!!.id.toString()),"")}")
//
//                                }
//                                "/get" ->{}
//                                "/jf" ->{
////                                    CoroutineScope(Dispatchers.IO).launch {
////                                        logService.log("${cmd} Start ${cmds[1]}")
////                                        String.format(String(Base64.getMimeDecoder().decode("aHR0cHM6Ly9qYXZtb3N0LnRvL3NlYXJjaC9tb3ZpZS8lcw==".toByteArray())),cmds[1]).getJ().let { doc -> FeedParseManager.parse(doc,rssDataService) }
////                                        logService.log("${cmd} END ${cmds[1]}")
////                                    }
////                                    CoroutineScope(Dispatchers.IO).launch {
////                                        logService.log("on Cmd JF with SO")
////                                        logService.log("${cmd} Start ${cmds[1]}")
////                                        String.format(String(Base64.getMimeDecoder().decode("aHR0cHM6Ly9rcjcwLnNvZ2lybC5zby8/cz0lcw==".toByteArray())),cmds[1]).getJ().let { doc -> FeedParseManager.parse(doc,rssDataService)}
////                                        logService.log("${cmd} END ${cmds[1]}")
////                                    }
//                                }
//                                "/lama" -> {
//                                    val req = BumlamaReq(msg.text!!.replace(cmd,""))
//                                    CoroutineScope(Dispatchers.IO).launch {
//
//                                        val fullUrl =
//                                            "https://api.telegram.org/${globalEvv.telegramBotKey}/sendMessage?chat_id=${globalEvv.telegramMyId}&text=lama 에게 전송 ${req.reqMsg}"
//                                        logService.log("fullUrl >>> ${fullUrl}")
//                                        WebClient.create().get()
//                                            .uri(fullUrl)
//                                            .retrieve()
//                                            .bodyToMono(String::class.java).block()
//                                    }
//                                    CoroutineScope(Dispatchers.IO).launch {
//                                        logService.log("${cmd} Start ${cmds[1]}")
////                                        msg.chat?.id
//                                        try {
//                                            val client = WebClient.create()
//                                            client.post()
//                                                .uri(lamaGenerated)
//                                                .body(BodyInserters.fromValue(Gson().toJson(req)))
//                                                .retrieve()
//                                                .bodyToMono(String::class.java).timeout(Duration.ofSeconds(6000L)).block()?.let { result ->
//                                                    Gson().fromJson(result, BumlamaResp::class.java)?.let { sss ->
//                                                        println(Gson().toJson(sss))
//                                                        val fullUrl = "https://api.telegram.org/${globalEvv.telegramBotKey}/sendMessage?chat_id=${globalEvv.telegramMyId}&text=${sss.response}"
//                                                        logService.log("fullUrl >>> ${fullUrl}")
//                                                        WebClient.create().get()
//                                                            .uri(fullUrl)
//                                                            .retrieve()
//                                                            .bodyToMono(String::class.java).block() ?: "FAIL"
//                                                    }
//                                                }
//                                        } catch (e: Exception) {
//                                            e.printStackTrace()
//                                        }
//
//                                        logService.log("${cmd[0]} END ${cmd[1]}")
//                                    }
//                                }
//                                else -> {}
//                            }
//                        }
//                    }
                } else if (msg.text?.contains("어디") == true) {
                    msg.from?.id?.let { sendMsg(it.toString()) }
                } else {
                    println(msg.text)
                    val req = BumlamaReq(msg.text)
                    CoroutineScope(Dispatchers.IO).launch {
                        val fullUrl =
                            "https://api.telegram.org/${globalEvv.telegramBotKey}/sendMessage?chat_id=${globalEvv.telegramMyId}&text=blama 일시키겠=> '${req.reqMsg}'"
                        logService.log("fullUrl >>> ${fullUrl}")
                        WebClient.create().get()
                            .uri(fullUrl)
                            .retrieve()
                            .bodyToMono(String::class.java).block()
                    }
                    CoroutineScope(Dispatchers.IO).launch {
                        var originalQuery = msg.text ?: ""
                        lama.generateResponse(originalQuery.replace("오늘","오늘(${SimpleDateFormat("yyyy-MM-dd").format(Date())})"),msg.from?.id.toString())
                    }
                }

            }
            logService.log("test $httpServletRequest.requestURI")
        } catch (e : Exception) {
        }
        return "Success"
    }



    //    fun chatClient(): ChatClient {
//        return OllamaChatClient(OllamaApi("https://lama.lunaticbum.kr"))
//            .withDefaultOptions(
//                OllamaOptions.create()
//                    .withModel("phi4:14b")
//                    .withNumThread(5)
//                    .withSeed(5)
//                    .withTemperature(0.9f)
//            )
//    }
    @Autowired
    lateinit var lama : Lama


    @ResponseBody
    @GetMapping("query/{path}")
    fun googleQueryTest(@PathVariable path: String): String {
        var originalQuery = path
//        POST /collections
//
//        Content-Type: application/json
//
//        {
//            "name": "movies",
//            "vector_size": 3072,
//            "distance": "Cosine"
//        }

//        println(lama.makeCollection())

//        val gSearch = "https://psn.lunaticbum.kr/search?q=${originalQuery?.replace("오늘", SimpleDateFormat("yyyMMdd").format(Date()))}&language=auto&time_range=month&safesearch=0&categories=general&format=json"
//        println("gSearch >>> ${gSearch}")
//        var additionalInfo = StringBuffer()
//        additionalInfo.append("참고자료")
//        var idx = 0
//        WebClient.create().get()
//            .uri(gSearch)
//            .retrieve()
//            .bodyToMono(SearXng::class.java).timeout(Duration.ofMinutes(20L)).block()?.let { gsResult ->
//                gsResult.results?.filter { it.score > 0.5}?.forEach {
//                    additionalInfo.append(idx).append(":").append(Gson().toJson(it))
//                    idx += 1
//                }
//            }
        CoroutineScope(Dispatchers.IO).async {
            lama.generateResponse(originalQuery.replace("오늘","오늘(${SimpleDateFormat("yyyy-MM-dd").format(Date())})"))
        }
        return "TEST"
    }





    enum class LamaQueryType(val keywords : ArrayList<String>) {
        None(arrayListOf()),
        Search(arrayListOf("검색")),
        Weather(arrayListOf("날씨")),
        NearBy(arrayListOf("주변에","근처에")),
        Post(arrayListOf("POST","저장")),
    }

    class LamaQuery {
        var userQuery : String? = null
        var now = SimpleDateFormat("yyyy년MM월dd일 HH:mm:ss").format(Date())
        var userId : String? = null
        var queryType : LamaQueryType = LamaQueryType.None
        var req : BumlamaReq? = null
        var telegramBotKey : String? = null
        fun start() {
            req = BumlamaReq(userQuery)
            LamaQueryType.values().reversed().forEach { type ->
                type.keywords.forEach {
                    if (queryType.equals(LamaQueryType.None)) {
                        if(userQuery?.contains(it) == true) {
                            queryType = type
                        }
                    }
                }
            }
            when (queryType) {
//                LamaQueryType.None -> {
//
//                }
                LamaQueryType.Search -> {

                }
                LamaQueryType.Weather -> {

                }
                LamaQueryType.Post -> {

                }
                else -> {
                    askToLama()
                }
            }
        }

        fun searchInfo() {
            askToLama()
        }

        fun searchWeather() {
            askToLama()
        }

        fun searchNearBy() {
            askToLama()
        }




        fun askToLama() {
            CoroutineScope(Dispatchers.IO).launch {
                req?.let { req ->
                    val client = WebClient.create()
                    client.post()
                        .uri(lamaGenerated)
                        .body(BodyInserters.fromValue(Gson().toJson(req)))
                        .retrieve()
                        .bodyToMono(String::class.java).timeout(Duration.ofMinutes(20L)).block()?.let { result ->
                            Gson().fromJson(result, BumlamaResp::class.java)?.let { sss ->
                                CoroutineScope(Dispatchers.IO).launch {
                                    var toalmsg = "${userQuery}의 대답이 도착했어요.\n" + "${sss.response}"
                                    val fullUrl = "https://api.telegram.org/${telegramBotKey}/sendMessage"
                                    toalmsg.chunked(2048).forEach { chunkedMsg ->
                                        println("fullUrl >>> ${fullUrl}")
                                        var tlgSend = TelegramSendMsg(userId!!, chunkedMsg)
                                        WebClient
                                            .create()
                                            .post()
                                            .uri(fullUrl)
                                            .body(BodyInserters.fromValue(Gson().toJson(tlgSend)))
                                            .retrieve().bodyToMono(String::class.java).timeout(Duration.ofMinutes(20L))
                                            .block()?.let { result ->

                                            }
                                    }
                                }
                            }
                        }
                }
            }.start()
        }

    }

    fun sendSimpleMsg(telegramBotKey : String , userId : String, msg :String) {
        val fullUrl = "https://api.telegram.org/${telegramBotKey}/sendMessage"
        var tlgSend = TelegramSendMsg(userId, msg)
        WebClient
            .create()
            .post()
            .uri(fullUrl)
            .body(BodyInserters.fromValue(Gson().toJson(tlgSend)))
            .retrieve().bodyToMono(String::class.java).timeout(Duration.ofMinutes(20L))
            .block()?.let { result ->

            }
    }




    @Bean
    @Scheduled(cron = "0 0 0/1 * * *") //
    fun runJob() {
        try {
            logService.log("telegramBotKey >>>> ${globalEvv.telegramBotKey}")
            logService.log("telegramMyId >>>> ${globalEvv.telegramMyId}")
            logService.log("weatherApiKey >>>> ${globalEvv.weatherApiKey}")
            if (
                ((globalEvv.weatherApiKey?.length ?: 0) > 3) &&
                ((globalEvv.telegramBotKey?.length ?: 0) > 3) &&
                ((globalEvv.telegramMyId?.length ?: 0) > 3)
            ) {
                locationLogService.getLocationLog()?.let {
                    try {
                        WebClient.create().get()
                            .uri("http://api.weatherapi.com/v1/current.json?key=${globalEvv.weatherApiKey}&q=${it.mLatitude},${it.mLongitude}&aqi=no")
                            .retrieve()
                            .bodyToMono(String::class.java)
                            .timeout(Duration.ofSeconds(30L))
                            .block()?.let { result ->
                                Gson().fromJson(result, CurrentWeather::class.java)?.let { sss ->
                                    val fullUrl = "https://api.telegram.org/${globalEvv.telegramBotKey}/sendMessage?chat_id=${globalEvv.telegramMyId}&text=${sss.getSummaryInfo(BigDecimal(it.mLatitude).setScale(3, RoundingMode.HALF_UP).toString(),BigDecimal(it.mLongitude).setScale(3, RoundingMode.HALF_UP).toString())}"
                                    logService.log("fullUrl >>> ${fullUrl}")
                                    WebClient.create().get()
                                        .uri(fullUrl)
                                        .retrieve()
                                        .bodyToMono(String::class.java).block() ?: "FAIL"
                                }
                            }
                    }
                    catch (e : Exception) {

                    }
                }
            }
        }catch (e : Exception) {
            e.printStackTrace()
        }

    }



    fun sendMsg(target : String) {
        val client = WebClient.create()
        locationLogService.getLocationLog()?.let {
            client.get()
                .uri("https://api.telegram.org/${globalEvv.telegramBotKey}/sendMessage?chat_id=${target}&text=${it.timeString}\n${it.mAddressLines.first()}\nhttps://www.google.com/maps/search/?api=1&query=${it.mLatitude},${it.mLongitude}")
                .retrieve()
                .bodyToMono(String::class.java).block() ?: "FAIL"
        }

    }


}

fun before5Min(): Long {
    val cal: Calendar = Calendar.getInstance(TimeZone.getTimeZone("GMT"))
    cal.setTime(Date(System.currentTimeMillis()))
    cal.timeZone = TimeZone.getDefault()
    cal.add(Calendar.MINUTE, -10)
    return cal.timeInMillis
}


class BumlamaReq {
    private constructor()
    constructor(reqMsg: String?) {
        this.reqMsg = reqMsg
    }

    @SerializedName("prompt")
    var reqMsg : String? = ""
    var model : String = "phi4:14b"
    //        var format : String = "json"
    var stream = false
}

class BumlamaResp {

    var model : String? = ""//"phi4:14b",
    var created_at : String? = ""// "": "2025-02-13T06:38:53.619359Z",
    var response : String? = ""// "{  \n  \"response\": \"Hello! How can I assist you today?\"  \n}",
    var done : Boolean? = true
    var done_reason : String? =  "stop"
    var context : ArrayList<Long>? = arrayListOf()
    var total_duration : Long = 0L//: 1600246875,
    var load_duration : Long = 0L//: 27544792,
    var prompt_eval_count : Long = 0L//: 11,
    var prompt_eval_duration : Long = 0L//: 279000000,
    var eval_count : Long = 0L//: 19,
    var eval_duration : Long = 0L//: 1292000000
}

val lamaGenerated : String = "https://lama.lunaticbum.kr/api/generate"

data class TelegramSendMsg(
    @SerializedName("chat_id")
    val userId: String, // null을 허용하지 않음
    @SerializedName("text")
    val msg: String // null을 허용하지 않음
)

fun PlacesSearchResult.summary(currentLat : Double,currentLng: Double) : String {
    return "${name}\n총 리뷰수: ${userRatingsTotal}\n평점 : ${rating}\n거리 : \n${calculateDistance(currentLat, currentLng, geometry!!.location!!.lat, geometry!!.location!!.lng)}km\n링크:\n https://www.google.com/maps/search/?api=1&query=${geometry!!.location!!.lat}%2C${geometry!!.location!!.lng}&query_place_id=${placeId}"
}