package com.secspace.sms.helpers

import android.annotation.SuppressLint
import android.content.ContentValues
import android.content.Context
import android.provider.CallLog.Calls
import com.secspace.sms.activities.SimpleActivity
import com.secspace.sms.models.RecentCall
import com.simplemobiletools.commons.extensions.*
import com.simplemobiletools.commons.helpers.*
import com.simplemobiletools.commons.models.contacts.Contact

//最近通话记录
class RecentsHelper(private val context: Context) {
    private val COMPARABLE_PHONE_NUMBER_LENGTH = 9
    private val QUERY_LIMIT = 200
    private val contentUri = Calls.CONTENT_URI

    /**
     * 获取通话记录
     */
    fun getRecentCalls(groupSubsequentCalls: Boolean, maxSize: Int = QUERY_LIMIT, callback: (List<RecentCall>) -> Unit) {
        val privateCursor = context.getMyContactsCursor(favoritesOnly = false, withPhoneNumbersOnly = true)
        ensureBackgroundThread {
            if (!context.hasPermission(PERMISSION_READ_CALL_LOG)) {
                callback(ArrayList())
                return@ensureBackgroundThread
            }

            ContactsHelper(context).getContacts(showOnlyContactsWithNumbers = true) { contacts: java.util.ArrayList<Contact> ->
                val privateContacts = MyContactsContentProvider.getContacts(context, privateCursor)
                if (privateContacts.isNotEmpty()) {
                    contacts.addAll(privateContacts)
                }
                getRecents(contacts, groupSubsequentCalls, maxSize, callback = callback)
            }
        }
    }

    @SuppressLint("NewApi")
    private fun getRecents(contacts: List<Contact>, groupSubsequentCalls: Boolean, maxSize: Int, callback: (List<RecentCall>) -> Unit) {

        val recentCalls = mutableListOf<RecentCall>()
        var previousRecentCallFrom = ""
        var previousStartTS = 0
        val contactsNumbersMap = HashMap<String, String>()
        val contactPhotosMap = HashMap<String, String>()

        val projection = arrayOf(
            Calls._ID,
            Calls.NUMBER,
            Calls.CACHED_NAME,
            Calls.CACHED_PHOTO_URI,
            Calls.DATE,
            Calls.DURATION,
            Calls.TYPE,
            Calls.PHONE_ACCOUNT_ID,
            Calls.COUNTRY_ISO,
            Calls.IS_READ,
            Calls.CACHED_MATCHED_NUMBER
        )

        /*val accountIdToSimIDMap = HashMap<String, Int>()
        context.getAvailableSIMCardLabels().forEach {
            accountIdToSimIDMap[it.handle.id] = it.id
        }*/

        val cursor = if (isNougatPlus()) {
            // https://issuetracker.google.com/issues/175198972?pli=1#comment6
            val limitedUri = contentUri.buildUpon()
                .appendQueryParameter(Calls.LIMIT_PARAM_KEY, QUERY_LIMIT.toString())
                .build()
            val sortOrder = "${Calls.DATE} DESC"
            context.contentResolver.query(limitedUri, projection, null, null, sortOrder)
        } else {
            val sortOrder = "${Calls.DATE} DESC LIMIT $QUERY_LIMIT"
            context.contentResolver.query(contentUri, projection, null, null, sortOrder)
        }

        val contactsWithMultipleNumbers = contacts.filter { it.phoneNumbers.size > 1 }
        val numbersToContactIDMap = HashMap<String, Int>()
        contactsWithMultipleNumbers.forEach { contact ->
            contact.phoneNumbers.forEach { phoneNumber ->
                numbersToContactIDMap[phoneNumber.value] = contact.contactId
                numbersToContactIDMap[phoneNumber.normalizedNumber] = contact.contactId
            }
        }

        cursor?.use {
            if (!cursor.moveToFirst()) {
                return@use
            }

            do {
                val id = cursor.getIntValue(Calls._ID)
                var isUnknownNumber = false
                val number = cursor.getStringValueOrNull(Calls.NUMBER)
                if (number == null || number == "-1") {
                    isUnknownNumber = true
                }

                var name = cursor.getStringValueOrNull(Calls.CACHED_NAME)
                if (name.isNullOrEmpty() || name == "-1") {
                    name = number.orEmpty()
                }

                if (name == number && !isUnknownNumber) {
                    if (contactsNumbersMap.containsKey(number)) {
                        name = contactsNumbersMap[number]!!
                    } else {
                        val normalizedNumber = number.normalizePhoneNumber()
                        if (normalizedNumber!!.length >= COMPARABLE_PHONE_NUMBER_LENGTH) {
                            name = contacts.filter { it.phoneNumbers.isNotEmpty() }.firstOrNull { contact ->
                                val curNumber = contact.phoneNumbers.first().normalizedNumber
                                if (curNumber.length >= COMPARABLE_PHONE_NUMBER_LENGTH) {
                                    if (curNumber.substring(curNumber.length - COMPARABLE_PHONE_NUMBER_LENGTH) == normalizedNumber.substring(
                                            normalizedNumber.length - COMPARABLE_PHONE_NUMBER_LENGTH
                                        )
                                    ) {
                                        contactsNumbersMap[number] = contact.getNameToDisplay()
                                        return@firstOrNull true
                                    }
                                }
                                false
                            }?.name ?: number
                        }
                    }
                }

                if (name.isEmpty() || name == "-1") {
                    name = /*context.getString(R.string.unknown)*/ ""
                }

                var photoUri = cursor.getStringValue(Calls.CACHED_PHOTO_URI) ?: ""
                if (photoUri.isEmpty() && !number.isNullOrEmpty()) {
                    if (contactPhotosMap.containsKey(number)) {
                        photoUri = contactPhotosMap[number]!!
                    } else {
                        val contact = contacts.firstOrNull { it.doesContainPhoneNumber(number) }
                        if (contact != null) {
                            photoUri = contact.photoUri
                            contactPhotosMap[number] = contact.photoUri
                        }
                    }
                }

                val startTS = (cursor.getLongValue(Calls.DATE) / 1000L).toInt()
                if (previousStartTS == startTS) {
                    continue
                } else {
                    previousStartTS = startTS
                }

                val duration = cursor.getIntValue(Calls.DURATION)
                val type = cursor.getIntValue(Calls.TYPE)
//                val accountId = cursor.getStringValue(Calls.PHONE_ACCOUNT_ID)
                val simID = /*accountIdToSimIDMap[accountId] ?:*/ -1
                val neighbourIDs = mutableListOf<Int>()
                var specificNumber = ""
                var specificType = ""
                val sfFlag = cursor.getStringValue(Calls.COUNTRY_ISO)
                val export = cursor.getStringValue(Calls.IS_READ)
                val syncHideNumber = cursor.getStringValue(Calls.CACHED_MATCHED_NUMBER)

                val contactIdWithMultipleNumbers = numbersToContactIDMap[number]
                if (contactIdWithMultipleNumbers != null) {
                    val specificPhoneNumber =
                        contacts.firstOrNull { it.contactId == contactIdWithMultipleNumbers }?.phoneNumbers?.firstOrNull { it.value == number }
                    if (specificPhoneNumber != null) {
                        specificNumber = specificPhoneNumber.value
                        specificType = context.getPhoneNumberTypeText(specificPhoneNumber.type, specificPhoneNumber.label)
                    }
                }

                var hideNumber = ""
                /* number?.let {
                     PhoneUtils.phoneNumberFormat(it) { number ->
                         hideNumber = if (sfFlag == Constant.SFSY) {
                             number
                         } else {
                             ""
                         }
                     }
                 }*/

                var exportStr = false
                export?.let {
                    exportStr = it == Constant.EXPORT
                }

                var syncNumber = false
                syncHideNumber?.let {
                    syncNumber = it == Constant.HIDE
                }


                val recentCall = RecentCall(
                    id = id,
                    phoneNumber = number.orEmpty(),
                    name = name,
                    photoUri = photoUri,
                    startTS = startTS,
                    duration = duration,
                    type = type,
                    neighbourIDs = neighbourIDs,
                    simID = simID,
                    specificNumber = specificNumber,
                    specificType = specificType,
                    isUnknownNumber = isUnknownNumber,
                    isShunFeng = sfFlag == Constant.SFSY,
                    sfNumber = hideNumber,
                    isExport = exportStr,
                    isHidePhoneNumber = syncNumber
                )

                // if we have multiple missed calls from the same number, show it just once
                if (!groupSubsequentCalls || "$number$name$simID" != previousRecentCallFrom) {
                    recentCalls.add(recentCall)
                } else {
                    recentCalls.lastOrNull()?.neighbourIDs?.add(id)
                }

//                previousRecentCallFrom = "$number$name$simID"
            } while (cursor.moveToNext() && recentCalls.size < maxSize)
        }

        val blockedNumbers = context.getBlockedNumbers()

        val recentResult = recentCalls
            .filter { !context.isNumberBlocked(it.phoneNumber, blockedNumbers) }

        callback(recentResult)
    }

    /***
     * 更改顺丰标志位
     * 根据手机号将所有的改为掩码
     */
    fun updateRecent(id: Int, callback: () -> Unit = {}) {
        val content = ContentValues().apply {
            put(Calls.COUNTRY_ISO, Constant.SFSY)
        }
        val updateResult = context.contentResolver.update(contentUri, content, "_id = $id", null)
        if (updateResult == 1) {
            callback.invoke()
        }
    }

    /***
     * 更改导出标志位
     */
    fun updateRecentIsExport(id: Int) {
        val content = ContentValues().apply {
            put(Calls.IS_READ, Constant.EXPORT)
        }
        context.contentResolver.update(contentUri, content, "_id = $id", null)
    }

    /**
     * 相同的号码进行同步掩码操作
     */
    fun updateSyncHideNumber(id: Int) {
        val content = ContentValues().apply {
            put(Calls.CACHED_MATCHED_NUMBER, Constant.HIDE)
        }
        context.contentResolver.update(contentUri, content, "_id = $id", null)
    }

    fun removeRecentCalls(ids: List<Int>, callback: () -> Unit) {
        ensureBackgroundThread {
            ids.chunked(30).forEach { chunk ->
                val selection = "${Calls._ID} IN (${getQuestionMarks(chunk.size)})"
                val selectionArgs: Array<String> = chunk.map { it.toString() }.toTypedArray()
                context.contentResolver.delete(contentUri, selection, selectionArgs)
            }
            callback.invoke()
        }
    }

    @SuppressLint("MissingPermission")
    fun removeAllRecentCalls(activity: SimpleActivity, callback: () -> Unit) {
        activity.handlePermission(PERMISSION_WRITE_CALL_LOG) {
            if (it) {
                ensureBackgroundThread {
                    context.contentResolver.delete(contentUri, null, null)
                    callback()
                }
            }
        }
    }

    fun restoreRecentCalls(activity: SimpleActivity, objects: List<RecentCall>, callback: () -> Unit) {
        activity.handlePermission(PERMISSION_WRITE_CALL_LOG) {
            if (it) {
                ensureBackgroundThread {
                    val values = objects
                        .sortedBy { it.startTS }
                        .map {
                            ContentValues().apply {
                                put(Calls.NUMBER, it.phoneNumber)
                                put(Calls.TYPE, it.type)
                                put(Calls.DATE, it.startTS.toLong() * 1000L)
                                put(Calls.DURATION, it.duration)
                                put(Calls.CACHED_NAME, it.name)
                            }
                        }.toTypedArray()

                    context.contentResolver.bulkInsert(contentUri, values)
                    callback()
                }
            }
        }
    }
}
