package com.wirelesstextsyncer.wireless_text_syncer_android

import android.app.Activity
import android.graphics.Color
import android.graphics.PorterDuff
import android.graphics.Typeface
import android.graphics.drawable.GradientDrawable
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.util.Log
import android.view.Gravity
import android.view.View
import android.view.WindowInsets
import android.view.WindowManager
import android.view.inputmethod.InputMethodManager
import android.content.Context
import android.widget.EditText
import android.widget.FrameLayout
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.Switch
import android.widget.TextView
import org.json.JSONArray

class QuickSendActivity : Activity() {
    private lateinit var input: EditText
    private lateinit var sendButton: LinearLayout
    private lateinit var sendIcon: ImageView
    private lateinit var sendLabel: TextView
    private lateinit var connectionIcon: ImageView
    private lateinit var endpointText: TextView
    private lateinit var deviceNameText: TextView
    private lateinit var connectionPanel: LinearLayout
    private lateinit var connectionDot: View
    private lateinit var connectionTitle: TextView
    private lateinit var connectionSubtitle: TextView
    private lateinit var retryText: TextView
    private lateinit var scanProgress: View
    private lateinit var devicesContainer: LinearLayout
    private lateinit var appendEnterToggle: LinearLayout
    private lateinit var appendEnterSwitch: Switch
    private val connectionInfoTypeface by lazy {
        runCatching {
            Typeface.createFromAsset(assets, "flutter_assets/assets/fonts/WDXLLubrifontSC-Regular.ttf")
        }.getOrDefault(Typeface.DEFAULT)
    }
    private var scanning = false
    private var appendEnter = false

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        Log.d("WTS", "QuickSendActivity onCreate")
        appendEnter = getSharedPreferences(PREFS, Context.MODE_PRIVATE)
            .getBoolean(KEY_APPEND_ENTER, false)
        buildUi()
        input.requestFocus()
        input.post {
            val imm = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
            imm.showSoftInput(input, InputMethodManager.SHOW_IMPLICIT)
        }
    }

    private fun buildUi() {
        val root = FrameLayout(this)
        root.setBackgroundColor(0x66000000)
        root.setOnClickListener { finish() }
        root.setOnApplyWindowInsetsListener { view, insets ->
            val imeBottom = insets.getInsets(WindowInsets.Type.ime()).bottom
            val navBottom = insets.getInsets(WindowInsets.Type.navigationBars()).bottom
            val keyboardLift = (imeBottom - navBottom).coerceAtLeast(0)
            view.setPadding(0, 0, 0, keyboardLift)
            insets
        }
        window.setSoftInputMode(
            WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE or
                WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE
        )

        val sheet = LinearLayout(this)
        sheet.orientation = LinearLayout.VERTICAL
        sheet.setPadding(dp(20), dp(18), dp(20), dp(18))
        sheet.setBackgroundColor(Color.WHITE)
        sheet.setOnClickListener { }

        val topRow = LinearLayout(this)
        topRow.gravity = Gravity.CENTER_VERTICAL
        topRow.orientation = LinearLayout.HORIZONTAL
        sheet.addView(topRow, LinearLayout.LayoutParams(
            LinearLayout.LayoutParams.MATCH_PARENT,
            LinearLayout.LayoutParams.WRAP_CONTENT
        ))

        val connectionSummary = LinearLayout(this)
        connectionSummary.gravity = Gravity.CENTER_VERTICAL
        connectionSummary.orientation = LinearLayout.HORIZONTAL
        topRow.addView(connectionSummary, LinearLayout.LayoutParams(
            0,
            dp(64),
            1f
        ))

        connectionIcon = ImageView(this)
        connectionSummary.addView(connectionIcon, LinearLayout.LayoutParams(dp(40), dp(40)))

        val summaryCopy = LinearLayout(this)
        summaryCopy.orientation = LinearLayout.VERTICAL
        summaryCopy.gravity = Gravity.CENTER_VERTICAL
        connectionSummary.addView(summaryCopy, LinearLayout.LayoutParams(
            0,
            dp(64),
            1f
        ).apply {
            leftMargin = dp(10)
        })

        endpointText = TextView(this)
        endpointText.textSize = 21f
        endpointText.typeface = Typeface.create(connectionInfoTypeface, Typeface.BOLD)
        endpointText.setTextColor(Color.rgb(15, 23, 42))
        endpointText.includeFontPadding = false
        endpointText.maxLines = 1
        summaryCopy.addView(endpointText)

        deviceNameText = TextView(this)
        deviceNameText.textSize = 18f
        deviceNameText.typeface = connectionInfoTypeface
        deviceNameText.setTextColor(Color.rgb(100, 116, 139))
        deviceNameText.includeFontPadding = false
        deviceNameText.maxLines = 1
        summaryCopy.addView(deviceNameText, LinearLayout.LayoutParams(
            LinearLayout.LayoutParams.MATCH_PARENT,
            LinearLayout.LayoutParams.WRAP_CONTENT
        ).apply {
            topMargin = dp(2)
        })

        appendEnterToggle = LinearLayout(this)
        appendEnterToggle.gravity = Gravity.CENTER_VERTICAL
        appendEnterToggle.orientation = LinearLayout.HORIZONTAL
        appendEnterToggle.setPadding(dp(10), 0, dp(4), 0)
        appendEnterToggle.background = strokeDrawable(Color.WHITE, Color.rgb(226, 232, 240), dp(8).toFloat())
        appendEnterToggle.isClickable = true
        appendEnterToggle.isFocusable = true
        appendEnterToggle.setOnClickListener { toggleAppendEnter() }

        val appendEnterLabel = TextView(this)
        appendEnterLabel.text = "追加回车"
        appendEnterLabel.textSize = 13f
        appendEnterLabel.typeface = Typeface.DEFAULT_BOLD
        appendEnterLabel.setTextColor(Color.rgb(15, 23, 42))
        appendEnterToggle.addView(appendEnterLabel, LinearLayout.LayoutParams(
            LinearLayout.LayoutParams.WRAP_CONTENT,
            LinearLayout.LayoutParams.WRAP_CONTENT
        ))

        appendEnterSwitch = Switch(this)
        appendEnterSwitch.isChecked = appendEnter
        appendEnterSwitch.setOnCheckedChangeListener { _, isChecked -> setAppendEnter(isChecked) }
        appendEnterToggle.addView(appendEnterSwitch)
        topRow.addView(appendEnterToggle, LinearLayout.LayoutParams(
            LinearLayout.LayoutParams.WRAP_CONTENT,
            dp(44)
        ).apply {
            leftMargin = dp(10)
        })

        connectionPanel = LinearLayout(this)
        connectionPanel.orientation = LinearLayout.VERTICAL
        connectionPanel.setPadding(dp(14), dp(12), dp(14), dp(12))
        connectionPanel.background = strokeDrawable(Color.rgb(248, 250, 252), Color.rgb(226, 232, 240), dp(12).toFloat())
        sheet.addView(connectionPanel, LinearLayout.LayoutParams(
            LinearLayout.LayoutParams.MATCH_PARENT,
            LinearLayout.LayoutParams.WRAP_CONTENT
        ).apply {
            topMargin = dp(10)
        })

        val connectionHeader = LinearLayout(this)
        connectionHeader.gravity = Gravity.CENTER_VERTICAL
        connectionHeader.orientation = LinearLayout.HORIZONTAL
        connectionPanel.addView(connectionHeader, LinearLayout.LayoutParams(
            LinearLayout.LayoutParams.MATCH_PARENT,
            LinearLayout.LayoutParams.WRAP_CONTENT
        ))

        connectionDot = View(this)
        connectionHeader.addView(connectionDot, LinearLayout.LayoutParams(dp(10), dp(10)))

        val connectionCopy = LinearLayout(this)
        connectionCopy.orientation = LinearLayout.VERTICAL
        connectionHeader.addView(connectionCopy, LinearLayout.LayoutParams(
            0,
            LinearLayout.LayoutParams.WRAP_CONTENT,
            1f
        ).apply {
            leftMargin = dp(10)
        })

        connectionTitle = TextView(this)
        connectionTitle.textSize = 14f
        connectionTitle.typeface = Typeface.DEFAULT_BOLD
        connectionTitle.setTextColor(Color.rgb(15, 23, 42))
        connectionCopy.addView(connectionTitle)

        connectionSubtitle = TextView(this)
        connectionSubtitle.textSize = 12f
        connectionSubtitle.setTextColor(Color.rgb(100, 116, 139))
        connectionCopy.addView(connectionSubtitle, LinearLayout.LayoutParams(
            LinearLayout.LayoutParams.MATCH_PARENT,
            LinearLayout.LayoutParams.WRAP_CONTENT
        ).apply {
            topMargin = dp(2)
        })

        retryText = TextView(this)
        retryText.text = "重试"
        retryText.textSize = 13f
        retryText.typeface = Typeface.DEFAULT_BOLD
        retryText.setTextColor(Color.rgb(37, 99, 235))
        retryText.setPadding(dp(10), dp(6), dp(10), dp(6))
        retryText.isClickable = true
        retryText.isFocusable = true
        retryText.setOnClickListener { scanAndConnect() }
        connectionHeader.addView(retryText)

        scanProgress = View(this)
        scanProgress.background = roundedDrawable(Color.rgb(37, 99, 235), dp(2).toFloat())
        connectionPanel.addView(scanProgress, LinearLayout.LayoutParams(
            LinearLayout.LayoutParams.MATCH_PARENT,
            dp(3)
        ).apply {
            topMargin = dp(10)
        })

        devicesContainer = LinearLayout(this)
        devicesContainer.orientation = LinearLayout.VERTICAL
        connectionPanel.addView(devicesContainer, LinearLayout.LayoutParams(
            LinearLayout.LayoutParams.MATCH_PARENT,
            LinearLayout.LayoutParams.WRAP_CONTENT
        ).apply {
            topMargin = dp(10)
        })

        input = EditText(this)
        input.minLines = 3
        input.maxLines = 6
        input.hint = "输入要发送到电脑的文本"
        input.textSize = 16f
        input.setPadding(dp(14), dp(12), dp(14), dp(12))
        input.background = strokeDrawable(Color.WHITE, Color.rgb(226, 232, 240), dp(12).toFloat())
        sheet.addView(input, LinearLayout.LayoutParams(
            LinearLayout.LayoutParams.MATCH_PARENT,
            LinearLayout.LayoutParams.WRAP_CONTENT
        ).apply {
            topMargin = dp(12)
        })

        sendButton = LinearLayout(this)
        sendButton.gravity = Gravity.CENTER
        sendButton.orientation = LinearLayout.HORIZONTAL
        sendButton.setPadding(dp(16), 0, dp(16), 0)
        sendButton.background = roundedDrawable(Color.rgb(37, 99, 235), dp(26).toFloat())
        sendButton.isClickable = true
        sendButton.isFocusable = true
        sendButton.setOnClickListener { sendAndClose() }
        sendIcon = ImageView(this)
        sendIcon.setImageResource(R.drawable.ic_send_24)
        sendIcon.setColorFilter(Color.WHITE, PorterDuff.Mode.SRC_IN)
        sendLabel = TextView(this)
        sendLabel.text = "发送到电脑"
        sendLabel.textSize = 16f
        sendLabel.typeface = Typeface.DEFAULT_BOLD
        sendLabel.setTextColor(Color.WHITE)
        sendButton.addView(sendIcon, LinearLayout.LayoutParams(dp(20), dp(20)))
        sendButton.addView(sendLabel, LinearLayout.LayoutParams(
            LinearLayout.LayoutParams.WRAP_CONTENT,
            LinearLayout.LayoutParams.WRAP_CONTENT
        ).apply {
            leftMargin = dp(8)
        })
        sheet.addView(sendButton, LinearLayout.LayoutParams(
            LinearLayout.LayoutParams.MATCH_PARENT,
            dp(52)
        ).apply {
            topMargin = dp(12)
        })

        val params = FrameLayout.LayoutParams(
            FrameLayout.LayoutParams.MATCH_PARENT,
            FrameLayout.LayoutParams.WRAP_CONTENT,
            Gravity.BOTTOM
        )
        root.addView(sheet, params)
        setContentView(root)
        refreshState()
        autoScanIfDisconnected()
    }

    private fun refreshState() {
        val state = ConnectionService.stateMap()
        val connected = state["connected"] as? Boolean ?: false
        val connecting = state["connecting"] as? Boolean ?: false
        val host = state["host"] as? String ?: ""
        val port = state["port"] as? Int ?: 8181
        val name = state["name"] as? String ?: ""
        val lastError = state["lastError"] as? String
        updateConnectionSummary(connected, connecting, host, port, name)
        connectionPanel.visibility = if (connected) View.GONE else View.VISIBLE
        connectionDot.background = roundedDrawable(when {
            connecting || scanning -> Color.rgb(37, 99, 235)
            else -> Color.rgb(185, 28, 28)
        }, dp(5).toFloat())
        scanProgress.visibility = if (scanning || connecting) View.VISIBLE else View.GONE
        retryText.visibility = if (!connecting && !scanning) View.VISIBLE else View.GONE
        when {
            connecting -> {
                connectionTitle.text = "正在连接电脑"
                connectionSubtitle.text = "$host:$port"
            }
            scanning -> {
                connectionTitle.text = "正在寻找附近的电脑"
                connectionSubtitle.text = "发现一个会自动连接，多个会显示选择"
            }
            else -> {
                connectionTitle.text = lastError ?: "暂未连接电脑"
                connectionSubtitle.text = "将自动扫描局域网内的桌面端服务"
            }
        }
        devicesContainer.visibility = if (connected) View.GONE else devicesContainer.visibility
        sendButton.isEnabled = connected
        sendButton.alpha = if (connected) 1f else 0.48f
    }

    private fun updateConnectionSummary(
        connected: Boolean,
        connecting: Boolean,
        host: String,
        port: Int,
        name: String
    ) {
        val iconColor = when {
            connected -> Color.rgb(22, 163, 74)
            connecting -> Color.rgb(37, 99, 235)
            else -> Color.rgb(185, 28, 28)
        }
        connectionIcon.setImageResource(when {
            connected || connecting -> R.drawable.ic_sync_24
            else -> R.drawable.ic_sync_disabled_24
        })
        connectionIcon.setColorFilter(iconColor, PorterDuff.Mode.SRC_IN)
        endpointText.setTextColor(if (connected) Color.rgb(22, 101, 52) else Color.rgb(15, 23, 42))
        deviceNameText.setTextColor(if (connected) Color.rgb(21, 128, 61) else Color.rgb(100, 116, 139))
        endpointText.text = if (host.isBlank()) "未设置地址" else "$host:$port"
        deviceNameText.text = name.ifBlank { "Windows 桌面端" }
    }

    private fun scanAndConnect() {
        if (scanning) {
            return
        }

        scanning = true
        updateConnectionSummary(connected = false, connecting = true, host = "", port = 8181, name = "")
        connectionTitle.text = "正在寻找附近的电脑"
        connectionSubtitle.text = "发现一个会自动连接，多个会显示选择"
        connectionDot.background = roundedDrawable(Color.rgb(37, 99, 235), dp(5).toFloat())
        retryText.visibility = View.GONE
        scanProgress.visibility = View.VISIBLE
        devicesContainer.removeAllViews()
        devicesContainer.visibility = View.GONE

        Thread {
            val devices = DiscoveryClient.discover(applicationContext)
            runOnUiThread {
                scanning = false
                when (devices.size) {
                    0 -> {
                        updateConnectionSummary(connected = false, connecting = false, host = "", port = 8181, name = "")
                        connectionTitle.text = "没有发现桌面端"
                        connectionSubtitle.text = "确认电脑和手机在同一局域网后重试"
                        connectionDot.background = roundedDrawable(Color.rgb(185, 28, 28), dp(5).toFloat())
                        retryText.visibility = View.VISIBLE
                        scanProgress.visibility = View.GONE
                    }
                    1 -> connectTo(devices.single())
                    else -> showDeviceChoices(devices)
                }
            }
        }.start()
    }

    private fun autoScanIfDisconnected() {
        val state = ConnectionService.stateMap()
        val connected = state["connected"] as? Boolean ?: false
        val connecting = state["connecting"] as? Boolean ?: false
        if (!connected && !connecting) {
            scanAndConnect()
        }
    }

    private fun showDeviceChoices(devices: List<DiscoveredService>) {
        updateConnectionSummary(connected = false, connecting = true, host = "", port = 8181, name = "")
        connectionTitle.text = "选择要连接的电脑"
        connectionSubtitle.text = "发现 ${devices.size} 个桌面端服务"
        connectionDot.background = roundedDrawable(Color.rgb(37, 99, 235), dp(5).toFloat())
        retryText.visibility = View.VISIBLE
        scanProgress.visibility = View.GONE
        devicesContainer.removeAllViews()
        devicesContainer.visibility = View.VISIBLE

        devices.forEach { device ->
            devicesContainer.addView(createDeviceChoice(device), LinearLayout.LayoutParams(
                LinearLayout.LayoutParams.MATCH_PARENT,
                LinearLayout.LayoutParams.WRAP_CONTENT
            ).apply {
                bottomMargin = dp(8)
            })
        }
    }

    private fun createDeviceChoice(device: DiscoveredService): TextView {
        return TextView(this).apply {
            text = "${device.name}\n${device.host}:${device.port}"
            textSize = 14f
            setTextColor(Color.rgb(15, 23, 42))
            setPadding(dp(12), dp(10), dp(12), dp(10))
            background = strokeDrawable(Color.WHITE, Color.rgb(203, 213, 225), dp(10).toFloat())
            isClickable = true
            isFocusable = true
            setOnClickListener { connectTo(device) }
        }
    }

    private fun connectTo(device: DiscoveredService) {
        updateConnectionSummary(
            connected = false,
            connecting = true,
            host = device.host,
            port = device.port,
            name = device.name
        )
        connectionTitle.text = "正在连接电脑"
        connectionSubtitle.text = "${device.host}:${device.port}"
        connectionDot.background = roundedDrawable(Color.rgb(37, 99, 235), dp(5).toFloat())
        retryText.visibility = View.GONE
        scanProgress.visibility = View.VISIBLE
        devicesContainer.removeAllViews()
        devicesContainer.visibility = View.GONE
        ConnectionService.connect(this, device.host, device.port, device.name)
        Handler(Looper.getMainLooper()).postDelayed({ refreshState() }, 700)
        Handler(Looper.getMainLooper()).postDelayed({ refreshState() }, 1800)
    }

    private fun sendAndClose() {
        val text = input.text.toString()
        Log.d("WTS", "QuickSendActivity send length=${text.length}")
        if (text.isBlank()) {
            return
        }

        if (ConnectionService.sendText(text) && (!appendEnter || ConnectionService.sendEnter())) {
            saveHistory(text)
            sendButton.background = roundedDrawable(Color.rgb(22, 163, 74), dp(26).toFloat())
            sendIcon.setImageResource(R.drawable.ic_check_24)
            sendIcon.setColorFilter(Color.WHITE, PorterDuff.Mode.SRC_IN)
            sendLabel.text = "发送成功"
            Handler(Looper.getMainLooper()).postDelayed({
                hideKeyboard()
                finish()
            }, 520)
        } else {
            refreshState()
        }
    }

    private fun saveHistory(text: String) {
        if (text.trim().isEmpty()) {
            return
        }

        val prefs = getSharedPreferences(PREFS, Context.MODE_PRIVATE)
        val currentHistory = readHistory()
        val nextHistory = linkedSetOf(text)
        currentHistory
            .filter { it != text }
            .take(MAX_HISTORY_ITEMS - 1)
            .forEach { nextHistory.add(it) }

        prefs.edit()
            .putString(KEY_HISTORY, JSON_LIST_PREFIX + JSONArray(nextHistory.toList()).toString())
            .apply()
    }

    private fun readHistory(): List<String> {
        val rawValue = getSharedPreferences(PREFS, Context.MODE_PRIVATE).all[KEY_HISTORY]
        return when (rawValue) {
            is String -> decodeHistoryString(rawValue)
            is Set<*> -> rawValue.filterIsInstance<String>()
            else -> emptyList()
        }
    }

    private fun decodeHistoryString(value: String): List<String> {
        if (!value.startsWith(JSON_LIST_PREFIX)) {
            return emptyList()
        }

        return runCatching {
            val array = JSONArray(value.substring(JSON_LIST_PREFIX.length))
            List(array.length()) { index -> array.optString(index) }
                .filter { it.isNotEmpty() }
        }.getOrDefault(emptyList())
    }

    private fun toggleAppendEnter() {
        setAppendEnter(!appendEnter)
    }

    private fun setAppendEnter(value: Boolean) {
        if (appendEnter == value && appendEnterSwitch.isChecked == value) {
            return
        }

        appendEnter = value
        getSharedPreferences(PREFS, Context.MODE_PRIVATE)
            .edit()
            .putBoolean(KEY_APPEND_ENTER, appendEnter)
            .apply()
        updateAppendEnterToggle()
    }

    private fun updateAppendEnterToggle() {
        if (appendEnterSwitch.isChecked != appendEnter) {
            appendEnterSwitch.isChecked = appendEnter
        }
        appendEnterToggle.background = strokeDrawable(
            Color.WHITE,
            if (appendEnter) Color.rgb(147, 197, 253) else Color.rgb(226, 232, 240),
            dp(16).toFloat()
        )
    }

    private fun hideKeyboard() {
        val imm = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
        imm.hideSoftInputFromWindow(input.windowToken, 0)
    }

    private fun roundedDrawable(color: Int, radius: Float): GradientDrawable {
        return GradientDrawable().apply {
            shape = GradientDrawable.RECTANGLE
            setColor(color)
            cornerRadius = radius
        }
    }

    private fun strokeDrawable(fill: Int, stroke: Int, radius: Float): GradientDrawable {
        return GradientDrawable().apply {
            shape = GradientDrawable.RECTANGLE
            setColor(fill)
            setStroke(dp(1), stroke)
            cornerRadius = radius
        }
    }

    private fun dp(value: Int): Int {
        return (value * resources.displayMetrics.density).toInt()
    }

    companion object {
        private const val PREFS = "FlutterSharedPreferences"
        private const val KEY_APPEND_ENTER = "flutter.append_enter"
        private const val KEY_HISTORY = "flutter.send_history"
        private const val JSON_LIST_PREFIX = "VGhpcyBpcyB0aGUgcHJlZml4IGZvciBhIGxpc3Qu!"
        private const val MAX_HISTORY_ITEMS = 10
    }
}
