package com.amz.genie.adapters

import android.app.Activity
import android.content.Context
import android.content.Intent
import android.graphics.BitmapFactory
import android.net.Uri
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.webkit.MimeTypeMap
import android.widget.ImageButton
import android.widget.LinearLayout
import android.widget.TextView
import android.widget.Toast
import androidx.core.content.FileProvider
import androidx.core.view.isVisible
import androidx.recyclerview.widget.RecyclerView
import com.amz.genie.R
import com.amz.genie.activities.AttachmentPreviewActivity
import com.amz.genie.helpers.AttachmentDownloader
import com.amz.genie.helpers.AttachmentExtractor
import com.amz.genie.helpers.ChatMessageRenderer
import com.amz.genie.helpers.Preferences
import com.amz.genie.helpers.Preferences.API_URL
import com.amz.genie.models.ChatItem
import com.amz.genie.services.APIMain
import com.google.android.material.imageview.ShapeableImageView
import com.google.gson.JsonArray
import com.google.gson.JsonElement
import com.google.gson.JsonObject
import java.io.File
import java.util.Locale
import kotlin.concurrent.thread

class ChatAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>() {

    companion object {
        private const val VT_LEFT = 1
        private const val VT_RIGHT = 2

        const val JENIS_TEKS = 0
        const val JENIS_WAKTU = 1
        const val JENIS_TANGGAL = 2
        const val JENIS_JAM = 3
        const val JENIS_ANGKA = 4
        const val JENIS_PECAHAN = 5
        const val JENIS_STRING = 6
        const val JENIS_LIST = 10
        const val JENIS_LAMPIRAN = 11
    }

    private data class SimpleAtt(val url: String, val fileName: String, val label: String = "Lampiran")
    private val items = mutableListOf<ChatItem>()

    fun submitList(newItems: List<ChatItem>) {
        items.clear()
        items.addAll(newItems)
        notifyDataSetChanged()
    }

    fun prepend(oldItems: List<ChatItem>) {
        if (oldItems.isEmpty()) return
        items.addAll(0, oldItems)
        notifyItemRangeInserted(0, oldItems.size)
    }

    fun getFirstVisibleId(): String? = items.firstOrNull()?.id

    override fun getItemViewType(position: Int): Int =
        if (items[position].isMine) VT_RIGHT else VT_LEFT

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
        val inf = LayoutInflater.from(parent.context)
        return if (viewType == VT_RIGHT) {
            RightVH(inf.inflate(R.layout.item_chat_right, parent, false))
        } else {
            LeftVH(inf.inflate(R.layout.item_chat_left, parent, false))
        }
    }

    override fun getItemCount() = items.size

    override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
        val item = items[position]
        when (holder) {
            is LeftVH -> holder.bind(item)
            is RightVH -> holder.bind(item)
        }
    }

    // =========================
    // ViewHolders
    // =========================
    inner class LeftVH(v: View) : RecyclerView.ViewHolder(v) {
        private val tvHeader = v.findViewById<TextView>(R.id.tv_header_item_chat_left)
        private val tvMessage = v.findViewById<TextView>(R.id.tv_message_item_chat_left)
        private val tvTime = v.findViewById<TextView>(R.id.tv_time_item_chat_left)
        private val ivAvatar = v.findViewById<ShapeableImageView>(R.id.iv_avatar_item_chat_left)
        private val llAttachments = v.findViewById<LinearLayout>(R.id.ll_attachments_item_chat_left)
        private val llDetail = v.findViewById<LinearLayout>(R.id.ll_detail_item_chat_left)

        fun bind(item: ChatItem) {
            tvMessage.text = ChatMessageRenderer.render(item.message)
            tvTime.text = item.timeText ?: ""

            bindKomunikasiDetail(llDetail, item.message)

            val headerText = buildString {
                append(item.senderName ?: "")
                val jobOutlet = listOfNotNull(item.senderJob, item.senderOutlet).joinToString(" - ")
                if (jobOutlet.isNotBlank()) {
                    append(" • ")
                    append(jobOutlet)
                }
            }.trim()

            val showMeta = !item.isSameSenderAsPrev
            tvHeader.isVisible = showMeta
            ivAvatar.isVisible = showMeta
            tvHeader.text = headerText.ifBlank { "-" }

            bindAttachments(llAttachments, item.message)
        }
    }

    inner class RightVH(v: View) : RecyclerView.ViewHolder(v) {
        private val tvMessage = v.findViewById<TextView>(R.id.tv_message_item_chat_right)
        private val tvTime = v.findViewById<TextView>(R.id.tv_time_item_chat_right)
        private val llAttachments = v.findViewById<LinearLayout>(R.id.ll_attachments_item_chat_right)
        private val llDetail = v.findViewById<LinearLayout>(R.id.ll_detail_item_chat_right)

        fun bind(item: ChatItem) {
            tvMessage.text = ChatMessageRenderer.render(item.message)
            tvTime.text = item.timeText ?: ""
            bindKomunikasiDetail(llDetail, item.message)
            bindAttachments(llAttachments, item.message)
        }
    }

    // =========================
    // Attachments
    // =========================
    private fun fallbackExtractAttachments(msg: JsonObject?): List<SimpleAtt> {
        if (msg == null) return emptyList()

        fun arr(vararg keys: String) = keys.firstNotNullOfOrNull { k ->
            msg.get(k)?.takeIf { it.isJsonArray }?.asJsonArray
        }

        val a = arr("attachments", "lampiran", "files", "berkas") ?: return emptyList()

        val out = ArrayList<SimpleAtt>()
        for (i in 0 until a.size()) {
            val it = a[i]
            if (!it.isJsonObject) continue
            val o = it.asJsonObject

            val url =
                o.get("url")?.asStringSafe()
                    ?: o.get("file_url")?.asStringSafe()
                    ?: o.get("path")?.asStringSafe()
                    ?: ""

            val name =
                o.get("fileName")?.asStringSafe()
                    ?: o.get("filename")?.asStringSafe()
                    ?: o.get("name")?.asStringSafe()
                    ?: o.get("nama_file")?.asStringSafe()
                    ?: "Attachment"

            if (url.isNotBlank()) out.add(SimpleAtt(url = url, fileName = name))
        }
        return out
    }

    private fun extractAksiKomunikasiLampiran(msg: JsonObject?): List<SimpleAtt> {
        if (msg == null) return emptyList()
        val el = msg.get("aksi_komunikasi_lampiran") ?: return emptyList()

        val urls = mutableListOf<String>()

        fun push(u: String) {
            val s = u.trim()
            if (s.isBlank()) return
            urls.add(s)
        }

        try {
            when {
                // ✅ format kamu sekarang: object { "weirdKey": [ "path1", ... ], "weirdKey2": [..] }
                el.isJsonObject -> {
                    val o = el.asJsonObject

                    // case normal: {nilai:[...]} (kalau suatu saat kamu rapihin backend)
                    val nilai = o.get("nilai")
                    if (nilai != null) {
                        if (nilai.isJsonArray) {
                            for (i in 0 until nilai.asJsonArray.size()) push(nilai.asJsonArray[i].asStringSafe())
                        } else if (nilai.isJsonPrimitive) push(nilai.asString)
                    } else {
                        // case weird: ambil semua entry yang value-nya JsonArray
                        for ((_, v) in o.entrySet()) {
                            if (!v.isJsonArray) continue
                            val arr = v.asJsonArray
                            for (i in 0 until arr.size()) {
                                push(arr[i].asStringSafe())
                            }
                        }
                    }
                }

                // fallback kalau jadi array
                el.isJsonArray -> {
                    val arr = el.asJsonArray
                    for (i in 0 until arr.size()) {
                        val it = arr[i]
                        if (it.isJsonPrimitive) push(it.asString)
                        else if (it.isJsonObject) {
                            val o = it.asJsonObject
                            val u = o.get("isian")?.asStringSafe()
                                ?: o.get("url")?.asStringSafe()
                                ?: o.get("path")?.asStringSafe()
                                ?: ""
                            if (u.isNotBlank()) push(u)
                        }
                    }
                }
            }
        } catch (_: Exception) {}

        // dedup + mapping ke chip
        return urls.distinct().map { u ->
            SimpleAtt(url = u, fileName = fileNameFromUrl(u), label = "Lampiran")
        }
    }

    private fun bindAttachments(container: LinearLayout, msg: JsonObject?) {
        container.removeAllViews()

        val list = AttachmentExtractor.extractAll(msg)

        // ✅ ambil lampiran dari aksi_komunikasi_lampiran (komunikasi detail jenis 11)
        val aksiLampiran = extractAksiKomunikasiLampiran(msg)

        val fallback = if (list.isEmpty() && aksiLampiran.isEmpty()) fallbackExtractAttachments(msg) else emptyList()

        val merged = ArrayList<SimpleAtt>()

        // list dari extractor utama (kalau ada)
        for (att in list) {
            merged.add(SimpleAtt(url = att.url, fileName = att.fileName, label = att.label))
        }

        // lampiran komunikasi detail
        merged.addAll(aksiLampiran)

        // fallback legacy
        merged.addAll(fallback)

        if (merged.isEmpty()) {
            container.isVisible = false
            return
        }

        container.isVisible = true
        val inf = LayoutInflater.from(container.context)

        for (att in merged) {
            val chip = inf.inflate(R.layout.item_attachment_chip, container, false)
            val tvName = chip.findViewById<TextView>(R.id.tv_name_attachment_chip)
            val ibRemove = chip.findViewById<ImageButton>(R.id.ib_remove_attachment_chip)
            ibRemove.isVisible = false

            tvName.text = "${att.label} • ${att.fileName}"
            chip.setOnClickListener {
                openProtectedAttachment(container.context, att.url, att.fileName)
            }
            container.addView(chip)
        }
    }

    private fun fileNameFromUrl(url: String): String {
        val clean = url.substringBefore("?").substringBefore("#")
        return clean.substringAfterLast("/", clean)
    }

    private fun openProtectedAttachment(ctx: Context, url: String, fileName: String) {
        val rawToken = Preferences.getAccessToken(ctx).orEmpty()
        if (rawToken.isBlank()) {
            Toast.makeText(ctx, "Token kosong. Tidak bisa buka lampiran.", Toast.LENGTH_LONG).show()
            return
        }

        Toast.makeText(ctx, "Membuka lampiran...", Toast.LENGTH_SHORT).show()

        val fixedUrl = if (url.startsWith("http", true)) url else {
            API_URL.trimEnd('/') + "/uploads/" + url.trimStart('/')
        }

        thread {
            try {
                val file = AttachmentDownloader.downloadToCache(
                    ctx = ctx,
                    url = fixedUrl,
                    fileName = fileName,
                    token = Preferences.getAccessToken(ctx)!!
                )

                (ctx as? Activity)?.runOnUiThread {
                    openDownloadedFile(ctx, file, fileName)
                } ?: run {
                    openDownloadedFile(ctx, file, fileName, forceNewTask = true)
                }

            } catch (e: Exception) {
                (ctx as? Activity)?.runOnUiThread {
                    Toast.makeText(ctx, e.message ?: "Gagal buka lampiran", Toast.LENGTH_LONG).show()
                } ?: run {
                    Toast.makeText(ctx, e.message ?: "Gagal buka lampiran", Toast.LENGTH_LONG).show()
                }
            }
        }
    }

    private fun openDownloadedFile(ctx: Context, file: File, fileName: String, forceNewTask: Boolean = false) {
        val isImage = runCatching { BitmapFactory.decodeFile(file.absolutePath) != null }.getOrDefault(false)

        if (isImage) {
            val i = Intent(ctx, AttachmentPreviewActivity::class.java).apply {
                putExtra("local_path", file.absolutePath)
                putExtra("title", fileName.ifBlank { file.name })
                if (forceNewTask) addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
            }
            ctx.startActivity(i)
        } else {
            openLocalFile(ctx, file, fileName, forceNewTask)
        }
    }

    private fun openLocalFile(ctx: Context, file: File, fileName: String, forceNewTask: Boolean) {
        val uri: Uri = FileProvider.getUriForFile(ctx, "${ctx.packageName}.fileprovider", file)
        val mime = guessMime(fileName)

        val i = Intent(Intent.ACTION_VIEW).apply {
            setDataAndType(uri, mime)
            addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
            if (forceNewTask) addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
        }

        ctx.startActivity(Intent.createChooser(i, "Buka lampiran"))
    }

    private fun guessMime(fileName: String): String {
        val ext = fileName.substringAfterLast('.', "").lowercase(Locale.getDefault())
        val mime = MimeTypeMap.getSingleton().getMimeTypeFromExtension(ext)
        return mime ?: "application/octet-stream"
    }

    // =========================
    // ✅ Komunikasi Detail (FIX)
    // =========================
    private data class DetailField(val label: String, val value: String)

    private fun bindKomunikasiDetail(container: LinearLayout, msg: JsonObject?) {
        container.removeAllViews()
        if (msg == null) {
            container.isVisible = false
            return
        }

        val details = extractKomunikasiDetailFields(msg)
        if (details.isEmpty()) {
            container.isVisible = false
            return
        }

        container.isVisible = true
        val inf = LayoutInflater.from(container.context)

        for (d in details) {
            val row = inf.inflate(R.layout.item_komunikasi_detail_row, container, false)
            val tvLabel = row.findViewById<TextView>(R.id.tv_label)
            val tvValue = row.findViewById<TextView>(R.id.tv_value)

            tvLabel.text = d.label
            tvValue.text = d.value.ifBlank { "-" }

            container.addView(row)
        }
    }

    private fun extractKomunikasiDetailFields(msg: JsonObject): List<DetailField> {

        fun arrFrom(vararg paths: String): JsonArray? {
            for (p in paths) {
                var cur: JsonElement = msg
                var ok = true
                for (seg in p.split(".")) {
                    if (!cur.isJsonObject) { ok = false; break }
                    val o = cur.asJsonObject
                    if (!o.has(seg)) { ok = false; break }
                    cur = o.get(seg)
                }
                if (ok && cur.isJsonArray) return cur.asJsonArray
            }
            return null
        }

        fun JsonElement?.asStringSafeNullable(): String? {
            if (this == null || this.isJsonNull) return null
            return try {
                when {
                    this.isJsonPrimitive -> this.asString
                    this.isJsonArray -> this.asJsonArray.joinToString("\n") { it.asStringSafeNullable().orEmpty() }
                    this.isJsonObject -> {
                        val o = this.asJsonObject
                        val pick = listOf("nilai", "value", "teks", "string", "uraian", "isi")
                            .firstNotNullOfOrNull { k -> o.get(k)?.takeIf { !it.isJsonNull } }
                        pick?.asStringSafeNullable() ?: o.toString()
                    }
                    else -> this.toString()
                }
            } catch (_: Exception) { null }
        }

        fun elementToListText(el: JsonElement?): String? {
            if (el == null || el.isJsonNull) return null
            return try {
                when {
                    el.isJsonArray -> el.asJsonArray.joinToString("\n") { "• " + it.asStringSafe() }
                    el.isJsonPrimitive -> el.asString
                    el.isJsonObject -> {
                        val o = el.asJsonObject
                        val v = o.get("nilai")
                        if (v != null && v.isJsonArray)
                            v.asJsonArray.joinToString("\n") { "• " + it.asStringSafe() }
                        else el.toString()
                    }
                    else -> el.toString()
                }
            } catch (_: Exception) { null }
        }

        fun readFromDetailValues(kodeDetail: Int, jenisId: Int): String? {
            val dv = msg.get("detail_values")?.takeIf { it.isJsonObject }?.asJsonObject ?: return null
            val el = dv.get(kodeDetail.toString()) ?: return null
            return if (jenisId == JENIS_LIST) elementToListText(el)
            else el.asStringSafeNullable()
        }

        fun readTypedSingle(jenisId: Int): String? {
            return when (jenisId) {

                JENIS_TANGGAL -> {
                    msg.get("aksi_komunikasi_tanggal")
                        ?.takeIf { it.isJsonObject }
                        ?.asJsonObject
                        ?.get("nilai")
                        .asStringSafeNullable()
                }

                JENIS_WAKTU -> {
                    msg.get("aksi_komunikasi_waktu")
                        ?.takeIf { it.isJsonObject }
                        ?.asJsonObject
                        ?.get("nilai")
                        .asStringSafeNullable()
                }

                JENIS_JAM -> {
                    msg.get("aksi_komunikasi_jam")
                        ?.takeIf { it.isJsonObject }
                        ?.asJsonObject
                        ?.get("nilai")
                        .asStringSafeNullable()
                }

                JENIS_ANGKA -> {
                    msg.get("aksi_komunikasi_angka")
                        ?.takeIf { it.isJsonObject }
                        ?.asJsonObject
                        ?.get("nilai")
                        .asStringSafeNullable()
                }

                JENIS_PECAHAN -> {
                    msg.get("aksi_komunikasi_pecahan")
                        ?.takeIf { it.isJsonObject }
                        ?.asJsonObject
                        ?.get("nilai")
                        .asStringSafeNullable()
                }

                JENIS_TEKS, JENIS_STRING -> {
                    msg.get("aksi_komunikasi_teks")
                        ?.takeIf { it.isJsonObject }
                        ?.asJsonObject
                        ?.get("nilai")
                        .asStringSafeNullable()
                        ?: msg.get("aksi_komunikasi_string")
                            ?.takeIf { it.isJsonObject }
                            ?.asJsonObject
                            ?.get("nilai")
                            .asStringSafeNullable()
                }

                JENIS_LIST -> {
                    val el = msg.get("aksi_komunikasi_list") ?: return null
                    extractListFromWeirdPayload(el)
                }

                JENIS_LAMPIRAN -> {
                    val atts = extractAksiKomunikasiLampiran(msg)
                    if (atts.isEmpty()) return null
                    atts.joinToString("\n") { "• ${it.fileName}" }
                }

                else -> null
            }
        }

        val detailArr = arrFrom(
            "komunikasi.komunikasi_detail",
            "komunikasi_detail",
            "aksi.komunikasi.komunikasi_detail"
        ) ?: return emptyList()

        val out = ArrayList<DetailField>()

        for (i in 0 until detailArr.size()) {
            val d = detailArr[i]
            if (!d.isJsonObject) continue
            val dobj = d.asJsonObject

            val label = dobj.get("isian").asStringSafe().ifBlank { "Field" }

            val kodeDetail =
                dobj.get("kode")?.asIntSafe()
                    ?: dobj.get("kode_komunikasi_detail")?.asIntSafe()
                    ?: continue

            val jenisObj = dobj.get("jenis_isian")
                ?.takeIf { it.isJsonObject }
                ?.asJsonObject

            val jenisId =
                jenisObj?.get("id")?.asIntSafe()
                    ?: dobj.get("id_jenis_isian")?.asIntSafe()
                    ?: 0

            val value =
                readFromDetailValues(kodeDetail, jenisId)
                    ?: readTypedSingle(jenisId)
                    ?: ""

            if (value.isNotBlank()) {
                out.add(DetailField(label, value.trim()))
            }
        }

        return out
    }

    private fun extractListFromWeirdPayload(el: JsonElement): String? {
        try {
            // Case 1: langsung array
            if (el.isJsonArray) {
                val arr = el.asJsonArray
                val values = mutableListOf<String>()
                for (i in 0 until arr.size()) {
                    val it = arr[i]
                    when {
                        it.isJsonPrimitive -> values.add(it.asString)
                        it.isJsonObject -> {
                            val o = it.asJsonObject
                            val v = o.get("nilai")?.let { x ->
                                if (x.isJsonPrimitive) x.asString else x.toString()
                            }
                            if (!v.isNullOrBlank()) values.add(v)
                        }
                    }
                }
                return values.takeIf { it.isNotEmpty() }?.joinToString("\n") { "• $it" }
            }

            // Case 2: object normal {nilai:[...]} atau {baris:[...], nilai:[...]}
            if (el.isJsonObject) {
                val o = el.asJsonObject

                // 2a) standar: ada key "nilai"
                val nilai = o.get("nilai")
                if (nilai != null) {
                    return extractListFromWeirdPayload(nilai)
                }

                // 2b) payload kamu: key nya string dict -> value nya JsonArray
                // ambil semua value yang JsonArray, gabung
                val merged = mutableListOf<String>()
                for ((_, v) in o.entrySet()) {
                    if (!v.isJsonArray) continue
                    val arr = v.asJsonArray
                    for (i in 0 until arr.size()) {
                        val it = arr[i]
                        if (it.isJsonNull) continue
                        val s = if (it.isJsonPrimitive) it.asString else it.toString()
                        if (s.isNotBlank()) merged.add(s)
                    }
                }
                return merged.takeIf { it.isNotEmpty() }?.joinToString("\n") { "• $it" }
            }

            return null
        } catch (_: Exception) {
            return null
        }
    }

    // =========================
    // Helpers Json Safe
    // =========================
    private fun JsonElement.asStringSafe(): String {
        return try {
            if (isJsonNull) "" else if (isJsonPrimitive) asString else toString()
        } catch (_: Exception) {
            ""
        }
    }

    private fun JsonElement.asIntSafe(): Int? {
        return try {
            if (isJsonNull) null else if (isJsonPrimitive) asInt else null
        } catch (_: Exception) {
            null
        }
    }
}