package rasel.lunar.launcher.workers

import android.annotation.SuppressLint
import android.content.ContentResolver
import android.content.Context
import android.database.Cursor
import android.net.Uri
import android.provider.Telephony
import androidx.work.WorkerParameters
import com.google.gson.Gson
import io.realm.kotlin.ext.query
import io.realm.kotlin.ext.realmListOf
import io.realm.kotlin.types.RealmList
import io.realm.kotlin.types.RealmObject
import io.realm.kotlin.types.annotations.Ignore
import io.realm.kotlin.types.annotations.PrimaryKey
import rasel.lunar.launcher.LauncherActivity.Companion.lActivity
import rasel.lunar.launcher.utils.BLog
import rasel.lunar.launcher.utils.beforeDay
import rasel.lunar.launcher.utils.getContactName
import java.io.BufferedReader
import java.io.IOException
import java.io.InputStream
import java.io.InputStreamReader
import java.util.Date

class RecentSmsGetter : BaseGetter {
    companion object {
        var dayRange = BaseGetter.defaultDay
    }
    constructor(context: Context, workerParams: WorkerParameters) : super(context, workerParams) {

    }




    @SuppressLint("RestrictedApi")
    override fun realWork(): Result {
        var dateParam = beforeDay(Date(),dayRange).toString()
        val managedCursor = lActivity?.contentResolver?.query(
            Telephony.Sms.CONTENT_URI, arrayOf(
                Telephony.Sms.THREAD_ID,
                Telephony.Sms.ADDRESS,
                Telephony.Sms.TYPE,
                Telephony.Sms.DATE,
                Telephony.Sms.DATE_SENT,
                Telephony.Sms.BODY,
                Telephony.Sms.PERSON,
            ), Telephony.Sms.DATE + "> ${dateParam}", null,  Telephony.Sms.DEFAULT_SORT_ORDER)
        if (managedCursor != null && managedCursor.isClosed == false) {
            try {
                val tid  = managedCursor.getColumnIndex(Telephony.Sms.THREAD_ID)
                val address = managedCursor.getColumnIndex(Telephony.Sms.ADDRESS)
                val type = managedCursor.getColumnIndex(Telephony.Sms.TYPE)
                val date = managedCursor.getColumnIndex(Telephony.Sms.DATE)
                val sendDate  = managedCursor.getColumnIndex(Telephony.Sms.DATE_SENT)
                val bodyIdx = managedCursor.getColumnIndex(Telephony.Sms.BODY)
                val name = managedCursor.getColumnIndex(Telephony.Sms.PERSON)
                while (managedCursor.moveToNext()) {
                    val id = managedCursor.getString(tid) // mobile number
                    val phNumber = managedCursor.getString(address) // mobile number
                    val callType = managedCursor.getString(type) // call type
                    val reciveDate = managedCursor.getLong(date) // call date
                    val sendedDate = managedCursor.getLong(sendDate) // call date
                    val smsBody = managedCursor.getString(bodyIdx).replace("\n"," ")
                    val callerName = managedCursor.getString(name)

                    var dir: String = ""
                    val dircode = callType.toInt()
                    when (dircode) {
                        Telephony.Sms.MESSAGE_TYPE_ALL -> {dir = "MESSAGE_TYPE_ALL"}
                        Telephony.Sms.MESSAGE_TYPE_INBOX -> {dir = "MESSAGE_TYPE_INBOX"}
                        Telephony.Sms.MESSAGE_TYPE_SENT -> {dir = "MESSAGE_TYPE_SENT"}
                        Telephony.Sms.MESSAGE_TYPE_DRAFT -> {dir = "MESSAGE_TYPE_DRAFT"}
                        Telephony.Sms.MESSAGE_TYPE_OUTBOX -> {dir = "MESSAGE_TYPE_OUTBOX"}
                        Telephony.Sms.MESSAGE_TYPE_FAILED -> {dir = "MESSAGE_TYPE_FAILED"}
                        Telephony.Sms.MESSAGE_TYPE_QUEUED -> {dir = "MESSAGE_TYPE_QUEUED"}
                    }
                    var log = RecentSms(
                        id.plus("_").plus(reciveDate).plus("_").plus(sendedDate),
                        phNumber,
                        dir,
                        reciveDate,
                        sendedDate,
                        smsBody,
                        callerName ?: ""
                    )
                    log.isMms = false
//                    BLog.LOGE("RecentSmsGetter resultData put ${phNumber +"_"+ reciveDate} >>> ${log.toJson()}")
                    log.sender = getContactName(applicationContext.contentResolver,phNumber) ?: ""
                    WorkersDb.getRealm().apply {
                        if (query<RecentSms>("uniqKey == $0", log.uniqKey).find().size == 0) {
                            writeBlocking {
                                copyToRealm(log)
                            }
                        }
                    }

                }
            } catch (e: Exception) {

            } finally {
                managedCursor.close()
            }
        }
        if (lActivity?.contentResolver != null) {
            MmsQueryHelper(lActivity?.contentResolver!!).query()
        }
        return Result.success().apply {
            dayRange = BaseGetter.defaultDay
        }
    }

}

internal class RecentSms : RealmObject {
    @PrimaryKey
    var uniqKey : String = ""

    var id : String = ""
    var isMms : Boolean = false
    var addr : String = ""
    var type : String = ""
    var rcvDate : Long = 0L
    var pstDate : Long = 0L
    var body : String = ""
    var person : String = ""
    var images : RealmList<String> = realmListOf()
    var videos : RealmList<String> = realmListOf()
    var audios : RealmList<String> = realmListOf()
    var texts : RealmList<String> = realmListOf()
    @Ignore
    var mmsContents : HashMap<String?,ArrayList<String>> = hashMapOf()
    var sender : String = ""

    constructor(
        id : String,
        addr: String,
        type: String,
        rcvDate: Long,
        pstDate: Long,
        body: String,
        person: String
    ) {
        this.id = id
        this.uniqKey =  id.plus("_").plus(addr).plus("_").plus(Math.max(rcvDate,pstDate)).plus("_SMS")
//        BLog.LOGE("sender ${sender} ::::: Id >> ${id} , addr ${addr}")
        this.addr = addr
        this.type = type
        this.rcvDate = rcvDate
        this.pstDate = pstDate
        this.body = body
        this.person = person
        this.isMms = false
    }

    constructor(id: String, sender: String, date: Long, body: HashMap<String?,ArrayList<String>>) {
        this.id = id
        this.uniqKey =  body.get("partId")!!.joinToString("_").plus(sender.plus("_").plus(date).plus("_MMS"))
//        BLog.LOGE("sender ${sender} ::::: partId >> ${body.get("partId")!!.joinToString("_")}")
        this.rcvDate = date
        this.mmsContents = body
        this.images.addAll(mmsContents.get("image")!!)
        this.audios.addAll(mmsContents.get("audio")!!)
        this.texts.addAll(mmsContents.get("text")!!)
        this.videos.addAll(mmsContents.get("video")!!)


        this.addr = sender
        this.isMms = true
    }

    constructor()

    fun toJson() : String {
        return Gson().toJson(this)
    }
}

internal class MmsQueryHelper(
    private val _contentResolver: ContentResolver
) {

    private val mmsUri: Uri = Telephony.Mms.CONTENT_URI
    private val cursor: Cursor?
        get() =
            _contentResolver.query(
                mmsUri,
                null,
                null,
                null,
                null
            )

    fun dataMapper(cursor: Cursor): RecentSms {
        val id = cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Mms._ID))
        val date = cursor.getLong(cursor.getColumnIndexOrThrow(Telephony.Mms.DATE)) * 1000
        val sender : String = getSender(id) ?: throw NullPointerException("NotFound Sender For Mms")
        val body = getBody(id) ?: throw NullPointerException("NotFound Body by Mms")

        return RecentSms(
            id = id.toString(),
            date = date,
            body = body,
            sender = sender
        ).apply {
            isMms = true
            this.sender = getContactName(_contentResolver,addr) ?: ""
        }
    }

    private fun getSender(id: Int): String? {
        _contentResolver.query(
            Uri.parse("content://mms/$id/addr"),
            null,
            "${Telephony.Mms.Addr.MSG_ID} = ?",
            arrayOf(id.toString()),
            null
        )?.use { senderAddressCursor ->
            if (senderAddressCursor.moveToFirst()) {
                do {
                    // 137 수신, 151 발신 타입 값이다. Telephony.Mms.Addr.Type 의 주석 참고.
                    val isSender = senderAddressCursor.getInt(
                        senderAddressCursor.getColumnIndexOrThrow(Telephony.Mms.Addr.TYPE)
                    ) == 137
                    var sender = senderAddressCursor.getString(
                        senderAddressCursor.getColumnIndexOrThrow(Telephony.Mms.Addr.ADDRESS)
                    )
//                    BLog.LOGE("sender >> ${sender}")
                    if (isSender) {
                        return sender
                    }
                } while (senderAddressCursor.moveToNext())
            }
        }
        return null
    }

    private fun getMmsText(id: String): String {
        val partURI = Uri.parse("content://mms/part/$id")
        var `is`: InputStream? = null
        val sb = StringBuilder()
        try {
            `is` = _contentResolver.openInputStream(partURI)
            if (`is` != null) {
                val isr = InputStreamReader(`is`, "UTF-8")
                val reader = BufferedReader(isr)
                var temp = reader.readLine()
                while (temp != null) {
                    sb.append(temp)
                    temp = reader.readLine()
                }
            }
        } catch (e: IOException) {
        } finally {
            if (`is` != null) {
                try {
                    `is`.close()
                } catch (e: IOException) {
                }
            }
        }
        return sb.toString()
    }
    private fun getBody(id: Int): HashMap<String?,ArrayList<String>> {
        var returns = hashMapOf<String?,ArrayList<String>>()
        val text = arrayListOf<String>()
        val image = arrayListOf<String>()
        val audio = arrayListOf<String>()
        val partsIds = arrayListOf<String>()
        partsIds.add(id.toString())
        val projection = arrayOf("*")
        val video = arrayListOf<String>()
        _contentResolver.query(
            Uri.parse("content://mms/part"),
            projection,
            "${Telephony.Mms.Part.MSG_ID} = ?",
            arrayOf(id.toString()),
            null
        )?.use { partsCursor ->
            if (partsCursor.moveToFirst()) {
                do {
                    val partId: String = partsCursor.getString(partsCursor.getColumnIndexOrThrow(
                        Telephony.Mms.Part._ID))
                    val partContentType =
                        partsCursor.getString(partsCursor.getColumnIndexOrThrow(Telephony.Mms.Part.CONTENT_TYPE))
//                    BLog.LOGE("partContentType >> ${partContentType}")
                    partsIds.add(partId.toString())
                    if(partContentType?.contains("text") == true) {
//                        BLog.LOGE("partContentType  text >> ${partContentType} :: ${partId}")
                        val data =
                            partsCursor.getString(partsCursor.getColumnIndexOrThrow(Telephony.Mms.Part._DATA))
                        val textBody =
                            partsCursor.getString(partsCursor.getColumnIndexOrThrow(Telephony.Mms.Part.TEXT))
                        if (data != null && data.length > 0) {
                            text.add(getMmsText(partId))
                        } else {
                            text.add(textBody)
                        }
                    }
                    else if(partContentType?.contains("image") == true) {
//                        BLog.LOGE("partContentType image >> ${partContentType}:: ${partId}")
                        image.add(partId)
                    }
                    else if(partContentType?.contains("audio") == true) {
//                        BLog.LOGE("partContentType audio >> ${partContentType}:: ${partId}")
                        audio.add(partId)
                    }
                    else if(partContentType?.contains("video") == true) {
//                        BLog.LOGE("partContentType video >> ${partContentType}:: ${partId}")
                        video.add(partId)
                    }
                    else  {
//                        BLog.LOGE("partContentType >> ${partContentType}:: ${partId}")
                    }

//                    if (partContentType == "text/plain") {
//                        var textBody =
//                            partsCursor.getString(partsCursor.getColumnIndexOrThrow(Telephony.Mms.Part.TEXT))
////                                .replace("\n\n", "\n").replace("\n", " ")
////                        if (textBody.length > 60) {
////                            textBody = textBody.substring(0, Math.min(textBody.length, 60)).plus(" ... ")
////                        }
////                        BLog.LOGE("textBody >>> ${textBody}")
//                        return Pair(textBody , partContentType)
//                    }
                } while (partsCursor.moveToNext())
            }
        }
        returns.put("partId", partsIds)
        returns.put("text", text)
        returns.put("image", image)
        returns.put("audio", audio)
        returns.put("video", video)
//        BLog.LOGE("returns.get(text).join => ${text.size}  :: ${text.joinToString("\n")}")
        return returns
    }

    fun convertData(cursor: Cursor?)  {
        cursor ?: return
        val dateTime = beforeDay(Date(),30)
        cursor.use {
            if (cursor.moveToFirst()) {
                do {
                    val data = kotlin.runCatching {
                        dataMapper(cursor)
                    }.getOrNull()
                    data?.let {
                        if (it?.pstDate ?: 0L >  dateTime || it?.rcvDate ?: 0L > dateTime) {
                            WorkersDb.getRealm().apply {
                                if (query<RecentSms>("uniqKey == $0", it.uniqKey).find().size == 0) {
                                    writeBlocking {
                                        copyToRealm(it)
                                    }
                                }
//                                else if (it.isMms && it.addr.contains("#CMAS#")){
//                                    writeBlocking {
//                                        it.uniqKey = it.uniqKey.plus("1")
//                                        copyToRealm(it)
//                                    }
//                                }
                            }
//                            smsList.add(it)
                        }
                    }
                } while (cursor.moveToNext())
            }
        }
    }
    fun query()  {
        return convertData(cursor)
    }

}