package io.nexilis.service.core

import android.content.Context
import android.util.Log
import io.newuniverse.SDK.nuSDKService
import io.nexilis.service.Service
import io.nexilis.service.apiScope
import io.nexilis.service.data.entities.Buddy
import io.nexilis.service.data.entities.Group
import io.nexilis.service.data.entities.GroupMember
import io.nexilis.service.data.entities.Message
import io.nexilis.service.data.entities.MessageStatus
import io.nexilis.service.data.rooms.ApiRoomDatabase
import io.nexilis.service.pass
import io.nexilis.service.tag
import kotlinx.coroutines.launch
import org.json.JSONArray
import java.util.zip.ZipInputStream

class Incoming private constructor() {

    private val context: Context by lazy { nuSDKService.getInstance(pass) as Context }

    companion object {

        @Volatile
        private var INSTANCE: Incoming? = null

        fun getInstance(): Incoming {
            return INSTANCE ?: synchronized(this) {
                val instance = Incoming()
                INSTANCE = instance
                instance
            }
        }
    }

    fun process(data: Data) {
        Log.d(tag, "process(${data.code}):$data")
        apiScope.launch {
            val callback = Service.map.remove(data.status)
            if (callback != null) {
                callback.invoke(this, data)
                return@launch
            }
            when (data.code) {
                "LF" -> {
                    loginFile(data)
                }

                "A037", "A037A" -> {
                    pushMySelf(data)
                }

                "A0052", "IBB", "A004" -> {
                    pushBuddies(data)
                }

                "IBG" -> {
                    pushGroupBatch(data)
                }

                "A009" -> {
                    pushMember(data)
                }

                "S0" -> {
                    pushMessage(data)
                }

                "INQ" -> {
                    inquiryMessage(data)
                }

                "A002" -> {
                    statusMessage(data)
                }

                else -> {
                    Log.d(tag, "unprocessed:$data")
                }
            }
        }
    }

    private fun loginFile(data: Data) {
        try {
            if (data.media.isNotEmpty()) {
                ZipInputStream(data.media.inputStream()).use { inputStream ->
                    generateSequence { inputStream.nextEntry }.filterNot { it.isDirectory }
                        .map { inputStream.bufferedReader().readText() }.toList()
                }.forEach {
                    it.lines().forEach { line ->
                        Log.d(tag, "loginFile >>>> $line")
                        val d = Data()
                        if (d.parse(line)) {
                            process(d)
                        } else {
                            Log.d(tag, "unknown data:$line")
                        }
                    }
                }
                Service.response(data.responseOk())
                Service.sendAsync(
                    Data(
                        code = "IFF",
                        status = System.nanoTime().toString(),
                        f_pin = data.f_pin
                    )
                )
            }
        } catch (e: Exception) {
            Log.e(tag, e.message, e)
            Service.response(data.responseNok())
        }
    }

    private suspend fun pushMySelf(data: Data) {
        try {
            val me = context.getSharedPreferences().getString("pin", "")
            val dao = ApiRoomDatabase.getDatabase(context).buddyDao()
            dao.deleteMe()
            dao.insert(
                Buddy(
                    data.bodies["A00"] ?: "",
                    data.bodies["A23"] ?: "",
                    data.bodies["A02"] ?: "",
                    data.bodies["A03"] ?: "",
                    data.bodies["A74"] ?: "",
                    data.bodies["B7"] ?: "",
                    data.bodies["A16"] ?: "",
                    data.bodies["A10"] ?: "",
                    data.bodies["A11"] ?: "",
                    data.bodies["A12"] ?: "",
                    data.bodies["A13"] ?: "",
                    data.bodies["A14"] ?: "",
                    data.bodies["A21"] ?: "",
                    data.bodies["A22"] ?: "",
                    "1",
                    data.bodies["A59"] ?: "",
                    data.bodies["A26"] ?: "",
                    data.bodies["A27"] ?: "",
                    data.bodies["B5"] ?: "",
                    data.bodies["B6"] ?: "",
                    data.bodies["A29"] ?: "",
                    data.bodies["A48"] ?: "",
                    data.bodies["A55"] ?: "",
                    "0",
                    "0",
                    data.bodies["A54"] ?: "",
                    data.bodies["tflr"] ?: "",
                    "0",
                    data.bodies["Bw"] ?: "",
                    data.bodies["A129"] ?: "",
                    "",
                    "",
                    "",
                    "",
                    data.bodies["A133"] ?: "",
                    data.bodies["A137"] ?: "",
                    "",
                    "",
                    data.bodies["A144"] ?: "",
                    "",
                    "",
                    data.bodies["BE_INFO"] ?: "",
                    data.bodies["ogi"] ?: "",
                    data.bodies["ogn"] ?: "",
                    data.bodies["ogt"] ?: "",
                    data.bodies["A156"] ?: "",
                    data.bodies["A157"] ?: "",
                    data.bodies["A154"] ?: "",
                    data.bodies["A155"] ?: "",
                    data.bodies["FL4"] ?: "",
                    data.bodies["FL5"] ?: "",
                    data.bodies["FL6"] ?: "",
                    data.bodies["A158"] ?: "",
                    data.bodies["A164"] ?: "",
                    data.bodies["A165"] ?: "",
                    data.bodies["A166"] ?: "",
                    data.bodies["A167"] ?: "",
                    data.bodies["A168"] ?: "",
                    data.bodies["A169"] ?: "",
                    data.bodies["certf"] ?: "",
                    data.bodies["oacc"] ?: "",
                    data.bodies["B7T"] ?: "",
                    data.bodies["RNAM"] ?: "",
                    data.bodies["ISA"] ?: "",
                    data.bodies["LSG"] ?: "",
                    data.bodies["AID"] ?: "",
                    data.bodies["ICP"] ?: "",
                    data.bodies["WKA"] ?: "",
                    data.bodies["ISL"] ?: ""
                )
            )
            Service.sendAck(me, data.status)
        } catch (e: Exception) {
            Log.e(tag, e.message, e)
        }
    }

    private suspend fun pushBuddies(data: Data) {
        try {
            val me = context.getSharedPreferences().getString("pin", "")
            val d = data.bodies["A112"] ?: ""
            val jsonArray = JSONArray(d)
            Log.d(tag, "pushBuddies:$jsonArray")
            for (i in 0 until jsonArray.length()) {
                val jsonObject = jsonArray.getJSONObject(i)
                ApiRoomDatabase.getDatabase(context).buddyDao().insert(
                    Buddy(
                        jsonObject.optString("A00") ?: "",
                        jsonObject.optString("A23") ?: "",
                        jsonObject.optString("A02") ?: "",
                        jsonObject.optString("A03") ?: "",
                        jsonObject.optString("A74") ?: "",
                        jsonObject.optString("B7") ?: "",
                        jsonObject.optString("A16") ?: "",
                        jsonObject.optString("A10") ?: "",
                        jsonObject.optString("A11") ?: "",
                        jsonObject.optString("A12") ?: "",
                        jsonObject.optString("A13") ?: "",
                        jsonObject.optString("A14") ?: "",
                        jsonObject.optString("A21") ?: "",
                        jsonObject.optString("A22") ?: "",
                        "0", // 0:friend, 1:self
                        jsonObject.optString("A59") ?: "",
                        jsonObject.optString("A26") ?: "",
                        jsonObject.optString("A27") ?: "",
                        jsonObject.optString("B5") ?: "",
                        jsonObject.optString("B6") ?: "",
                        jsonObject.optString("A29") ?: "",
                        jsonObject.optString("A48") ?: "",
                        jsonObject.optString("A55") ?: "",
                        jsonObject.optString("A43") ?: "",
                        "0",
                        jsonObject.optString("A54") ?: "",
                        jsonObject.optString("tflr") ?: "",
                        "1",
                        jsonObject.optString("Bw") ?: "",
                        jsonObject.optString("A129") ?: "",
                        "",
                        "",
                        "",
                        "",
                        jsonObject.optString("A133") ?: "",
                        jsonObject.optString("A137") ?: "",
                        jsonObject.optString("BT") ?: "",
                        jsonObject.optString("Bb") ?: "",
                        jsonObject.optString("A144") ?: "",
                        "",
                        "",
                        jsonObject.optString("BE_INFO") ?: "",
                        jsonObject.optString("ogi") ?: "",
                        jsonObject.optString("ogn") ?: "",
                        jsonObject.optString("ogt") ?: "",
                        jsonObject.optString("A156") ?: "",
                        jsonObject.optString("A157") ?: "",
                        jsonObject.optString("A154") ?: "",
                        jsonObject.optString("A155") ?: "",
                        jsonObject.optString("FL4") ?: "",
                        jsonObject.optString("FL5") ?: "",
                        jsonObject.optString("FL6") ?: "",
                        jsonObject.optString("A158") ?: "",
                        jsonObject.optString("A164") ?: "",
                        jsonObject.optString("A165") ?: "",
                        jsonObject.optString("A166") ?: "",
                        jsonObject.optString("A167") ?: "",
                        jsonObject.optString("A168") ?: "",
                        jsonObject.optString("A169") ?: "",
                        jsonObject.optString("certf") ?: "",
                        jsonObject.optString("oacc") ?: "",
                        jsonObject.optString("B7T") ?: "",
                        jsonObject.optString("RNAM") ?: "",
                        jsonObject.optString("ISA") ?: "",
                        jsonObject.optString("LSG") ?: "",
                        jsonObject.optString("AID") ?: "",
                        jsonObject.optString("ICP") ?: "",
                        jsonObject.optString("WKA") ?: "",
                        jsonObject.optString("ISL") ?: ""
                    )
                )
            }
            Service.sendAck(me, data.status)
        } catch (e: Exception) {
            Log.e(tag, e.message, e)
        }
    }

    private suspend fun pushGroupBatch(data: Data) {
        try {
            val me = context.getSharedPreferences().getString("pin", "")
            val d = data.bodies["A112"] ?: ""
            val jsonArray = JSONArray(d)
            for (i in 0 until jsonArray.length()) {
                val jsonObject = jsonArray.getJSONObject(i)
                ApiRoomDatabase.getDatabase(context).groupDao().insert(
                    Group(
                        jsonObject.optString("A04") ?: "",
                        jsonObject.optString("A05") ?: "",
                        jsonObject.optString("A06") ?: "",
                        jsonObject.optString("A74") ?: "",
                        jsonObject.optString("A16") ?: "",
                        jsonObject.optString("A11") ?: "0",
                        jsonObject.optString("A28") ?: "",
                        jsonObject.optString("A29") ?: "0",
                        jsonObject.optString("A43") ?: "",
                        "",
                        jsonObject.optInt("B0") ?: 0,
                        jsonObject.optInt("A122") ?: 0,
                        jsonObject.optString("BP") ?: "",
                        jsonObject.optString("A103") ?: "",
                        0,
                        jsonObject.optInt("IOP") ?: 0,
                        jsonObject.optInt("oacc") ?: 0,
                        jsonObject.optInt("lvledu") ?: 0,
                        jsonObject.optInt("mtredu") ?: 0,
                        jsonObject.optInt("IED") ?: 0,
                        jsonObject.optString("BZ1") ?: ""
                    )
                )
                val members = JSONArray(jsonObject.getString("member"))
                for (j in 0 until members.length()) {
                    val jsonMember = members.getJSONObject(j)
                    ApiRoomDatabase.getDatabase(context).groupMemberDao().insert(
                        GroupMember(
                            jsonMember.optString("A04") ?: "",
                            jsonMember.optString("A00") ?: "",
                            jsonMember.optString("A80") ?: "",
                            jsonMember.optString("B7") ?: "",
                            jsonMember.optString("AC") ?: "",
                            jsonMember.optString("AC_DESC") ?: "",
                            jsonMember.optString("A02") ?: "",
                            jsonMember.optString("A03") ?: "",
                            jsonMember.optString("B5") ?: "",
                            jsonMember.optString("A74") ?: "",
                            jsonMember.optString("A29") ?: System.currentTimeMillis().toString(),
                        )
                    )
                }
            }
            Service.sendAck(me, data.status)
        } catch (e: Exception) {
            Log.e(tag, e.message, e)
        }
    }

    private suspend fun pushMember(data: Data) {
        try {
            val me = context.getSharedPreferences().getString("pin", "")
            ApiRoomDatabase.getDatabase(context).groupMemberDao().insert(
                GroupMember(
                    data.bodies["A04"] ?: "",
                    data.bodies["A00"] ?: "",
                    data.bodies["A80"] ?: "",
                    data.bodies["B7"] ?: "",
                    data.bodies["AC"] ?: "",
                    data.bodies["AC_DESC"] ?: "",
                    data.bodies["A02"] ?: "",
                    data.bodies["A03"] ?: "",
                    data.bodies["B5"] ?: "",
                    data.bodies["A74"] ?: "",
                    data.bodies["A29"] ?: System.currentTimeMillis().toString(),
                )
            )
            Service.sendAck(me, data.status)
        } catch (e: Exception) {
            Log.e(tag, e.message, e)
        }
    }

    private suspend fun pushMessage(data: Data) {
        try {
            val messageId = data.bodies["A18"] ?: ""
            if (messageId.isEmpty()) return
            val me = context.getSharedPreferences().getString("pin", "")
            ApiRoomDatabase.getDatabase(context).messageDao().get(messageId).value?.let {
                Service.sendAck(me, data.status)
                return
            }
            val fPin = data.bodies["A00"] ?: ""
            val lPin = data.bodies["A01"] ?: ""
            val opposite = if (me == fPin) lPin else fPin
            val scope = data.bodies["A06"] ?: "3"
            val chatId = data.bodies["BA"] ?: ""
            ApiRoomDatabase.getDatabase(context).messageDao().insert(
                Message(
                    messageId,
                    fPin,
                    lPin,
                    scope,
                    data.bodies["A19"]?.toLong() ?: System.currentTimeMillis(),
                    data.bodies["A15"] ?: "",
                    data.bodies["A07"]?.toNormalString() ?: "",
                    data.bodies["A63"] ?: "",
                    data.bodies["A47"] ?: "",
                    data.bodies["A57"] ?: "",
                    data.bodies["A74"] ?: "",
                    opposite,
                    data.bodies["EX1"] ?: "",
                    data.bodies["A68"] ?: "",
                    data.bodies["B4"]?.toInt() ?: 0,
                    data.bodies["A52"] ?: "",
                    data.bodies["B8"] ?: "",
                    data.bodies["B9"] ?: "",
                    data.bodies["Bf"]?.toInt() ?: 0,
                    chatId,
                    data.bodies["BN"] ?: "",
                    0,
                    data.bodies["A117"] ?: "1",
                    data.bodies["A116"] ?: "",
                    data.bodies["A118"] ?: "",
                    data.bodies["A149"]?.toInt() ?: 0,
                    0,
                    data.bodies["BR"] ?: "",
                    data.bodies["A121"] ?: "",
                    data.bodies["STQ"]?.toInt() ?: 0,
                    0,
                    0,
                    data.bodies["ACQ"]?.toInt() ?: 0,
                    0,
                    "",
                    "",
                    "",
                    "",
                    "",
                    data.bodies["A36"] ?: "",
                    data.bodies["tgf"] ?: "",
                    data.bodies["tga"] ?: "",
                    0,
                    0,
                    data.bodies["tgc"] ?: "",
                    data.bodies["tgsa"] ?: "",
                    data.bodies["A150"]?.toInt() ?: 0,
                    data.bodies["mla"] ?: "",
                    data.bodies["tpl"] ?: "",
                    data.bodies["lcltm"]?.toLong() ?: System.currentTimeMillis(),
                    data.bodies["ics"]?.toInt() ?: 0,
                    data.bodies["icc"]?.toInt() ?: 0,
                    data.bodies["ccid"] ?: ""
                )
            )
            if (scope == "4") {
                val id = chatId.ifEmpty { lPin }
                ApiRoomDatabase.getDatabase(context).groupMemberDao().get(id).value?.let {
                    it.forEach { member ->
                        ApiRoomDatabase.getDatabase(context).messageStatusDao().insert(
                            MessageStatus(
                                message_id = messageId,
                                f_pin = member.f_pin,
                                user_id = member.f_pin,
                                last_update = System.currentTimeMillis(),
                                time_delivered = 0,
                                time_read = 0,
                                time_ack = 0,
                            )
                        )
                    }
                }
            } else {
                ApiRoomDatabase.getDatabase(context).messageStatusDao().insert(
                    MessageStatus(
                        message_id = messageId,
                        f_pin = opposite,
                        user_id = opposite,
                        last_update = System.currentTimeMillis(),
                        time_delivered = 0,
                        time_read = 0,
                        time_ack = 0,
                    )
                )
            }
            Service.sendAck(me, data.status)
        } catch (e: Exception) {
            Log.e(tag, e.message, e)
        }
    }

    private fun inquiryMessage(data: Data) {
        try {
            val messageId = data.bodies["A18"] ?: ""
            if (messageId.isEmpty()) return
            ApiRoomDatabase.getDatabase(context).messageDao().get(messageId).value?.let {
                Service.sendAsync(data.responseOk())
                return
            }
            Service.sendAsync(data.responseNok())
        } catch (e: Exception) {
            Log.e(tag, e.message, e)
        }
    }

    private suspend fun statusMessage(data: Data) {
        try {
            val me = context.getSharedPreferences().getString("pin", "")
            val messageIds = data.bodies["A18"] ?: ""
            if (messageIds.isEmpty()) return
            val status = data.bodies["A15"]?.toInt() ?: 0
            if (status == 0) return
            val opposite = data.bodies["A01"] ?: ""
            if (opposite.isEmpty()) return
            val time = data.bodies["A19"]?.toLong() ?: System.currentTimeMillis()
            messageIds.split(",").forEach {
                val messageId = it.trim()
                when (status) {
                    3 -> ApiRoomDatabase.getDatabase(context).messageStatusDao().updateDelivered(
                        status = status,
                        timeDelivered = time,
                        messageId = messageId,
                        pin = opposite
                    )

                    4 -> ApiRoomDatabase.getDatabase(context).messageStatusDao().updateRead(
                        status = status,
                        timeRead = time,
                        messageId = messageId,
                        pin = opposite
                    )

                    8 -> ApiRoomDatabase.getDatabase(context).messageStatusDao().updateAck(
                        status = status,
                        timeAck = time,
                        messageId = messageId,
                        pin = opposite
                    )

                    else -> ApiRoomDatabase.getDatabase(context).messageStatusDao().updateStatus(
                        status = status,
                        messageId = messageId,
                        pin = opposite
                    )
                }
            }
            Service.sendAck(me, data.status)
        } catch (e: Exception) {
            Log.e(tag, e.message, e)
        }
    }

}