package club.clubk.ktag.apps.core

import android.annotation.SuppressLint
import android.app.PendingIntent
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.hardware.usb.UsbDevice
import android.hardware.usb.UsbDeviceConnection
import android.hardware.usb.UsbManager
import android.os.Build
import android.util.Log
import com.hoho.android.usbserial.driver.UsbSerialDriver
import com.hoho.android.usbserial.driver.UsbSerialPort
import com.hoho.android.usbserial.driver.UsbSerialProber
import com.hoho.android.usbserial.util.SerialInputOutputManager
import java.io.IOException
import java.util.concurrent.Executors

class UsbSerialManager(
    private val context: Context,
    private val listener: Listener,
    private val actionUsbPermission: String = "club.clubk.ktag.apps.USB_PERMISSION"
) {
    interface Listener {
        fun onDataReceived(data: ByteArray)
        fun onConnectionStateChanged(state: ConnectionState)
        fun onError(message: String)
        fun onDevicesChanged(devices: List<UsbSerialDriver>)
        fun onAutoConnecting(driver: UsbSerialDriver)
    }

    enum class ConnectionState {
        DISCONNECTED,
        AWAITING_PERMISSION,
        CONNECTING,
        CONNECTED,
        ERROR
    }

    private val usbManager = context.getSystemService(Context.USB_SERVICE) as UsbManager
    private var usbSerialPort: UsbSerialPort? = null
    private var usbConnection: UsbDeviceConnection? = null
    private var ioManager: SerialInputOutputManager? = null
    private var pendingDriver: UsbSerialDriver? = null
    private var autoConnectEnabled = true

    private val serialListener = object : SerialInputOutputManager.Listener {
        override fun onNewData(data: ByteArray) {
            listener.onDataReceived(data)
        }

        override fun onRunError(e: Exception) {
            Log.e(TAG, "Serial run error", e)
            disconnect()
            listener.onError("Connection lost: ${e.message}")
        }
    }

    private val usbReceiver = object : BroadcastReceiver() {
        override fun onReceive(context: Context, intent: Intent) {
            when (intent.action) {
                actionUsbPermission -> {
                    synchronized(this) {
                        val device = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
                            intent.getParcelableExtra(UsbManager.EXTRA_DEVICE, UsbDevice::class.java)
                        } else {
                            @Suppress("DEPRECATION")
                            intent.getParcelableExtra(UsbManager.EXTRA_DEVICE)
                        }
                        if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
                            device?.let {
                                pendingDriver?.let { driver ->
                                    connectToDevice(driver)
                                }
                            }
                        } else {
                            listener.onConnectionStateChanged(ConnectionState.DISCONNECTED)
                            listener.onError("USB permission denied")
                        }
                        pendingDriver = null
                    }
                }
                UsbManager.ACTION_USB_DEVICE_ATTACHED -> {
                    val devices = refreshDevices()
                    // Auto-connect if enabled and not already connected
                    if (autoConnectEnabled && !isConnected() && devices.isNotEmpty()) {
                        val driver = devices.first()
                        Log.i(TAG, "Auto-connecting to ${driver.device.deviceName}")
                        listener.onAutoConnecting(driver)
                        connect(driver)
                    }
                }
                UsbManager.ACTION_USB_DEVICE_DETACHED -> {
                    val device = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
                        intent.getParcelableExtra(UsbManager.EXTRA_DEVICE, UsbDevice::class.java)
                    } else {
                        @Suppress("DEPRECATION")
                        intent.getParcelableExtra(UsbManager.EXTRA_DEVICE)
                    }
                    if (device != null && usbSerialPort?.device == device) {
                        disconnect()
                        listener.onError("Device disconnected")
                    }
                    refreshDevices()
                }
            }
        }
    }

    fun register() {
        val filter = IntentFilter().apply {
            addAction(actionUsbPermission)
            addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED)
            addAction(UsbManager.ACTION_USB_DEVICE_DETACHED)
        }
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
            context.registerReceiver(usbReceiver, filter, Context.RECEIVER_NOT_EXPORTED)
        } else {
            context.registerReceiver(usbReceiver, filter)
        }
        val devices = refreshDevices()
        // Auto-connect if a device is already plugged in
        if (autoConnectEnabled && devices.isNotEmpty()) {
            val driver = devices.first()
            Log.i(TAG, "Auto-connecting to already-attached device: ${driver.device.deviceName}")
            listener.onAutoConnecting(driver)
            connect(driver)
        }
    }

    fun unregister() {
        try {
            context.unregisterReceiver(usbReceiver)
        } catch (e: IllegalArgumentException) {
            // Receiver not registered
        }
        disconnect()
    }

    fun getAvailableDevices(): List<UsbSerialDriver> {
        return UsbSerialProber.getDefaultProber().findAllDrivers(usbManager)
    }

    fun refreshDevices(): List<UsbSerialDriver> {
        val devices = getAvailableDevices()
        listener.onDevicesChanged(devices)
        return devices
    }

    fun connect(driver: UsbSerialDriver) {
        if (!usbManager.hasPermission(driver.device)) {
            pendingDriver = driver
            listener.onConnectionStateChanged(ConnectionState.AWAITING_PERMISSION)
            val flags = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
                PendingIntent.FLAG_MUTABLE
            } else {
                0
            }
            val permissionIntent = PendingIntent.getBroadcast(
                context, 0, Intent(actionUsbPermission), flags
            )
            usbManager.requestPermission(driver.device, permissionIntent)
        } else {
            connectToDevice(driver)
        }
    }

    @SuppressLint("MissingPermission")
    private fun connectToDevice(driver: UsbSerialDriver) {
        listener.onConnectionStateChanged(ConnectionState.CONNECTING)
        try {
            val connection = usbManager.openDevice(driver.device)
            if (connection == null) {
                listener.onConnectionStateChanged(ConnectionState.ERROR)
                listener.onError("Failed to open device")
                return
            }

            val port = driver.ports[0]
            port.open(connection)
            port.setParameters(BAUD_RATE, DATA_BITS, UsbSerialPort.STOPBITS_1, UsbSerialPort.PARITY_NONE)

            usbConnection = connection
            usbSerialPort = port

            ioManager = SerialInputOutputManager(port, serialListener).apply {
                start()
            }

            listener.onConnectionStateChanged(ConnectionState.CONNECTED)
            Log.i(TAG, "Connected to ${driver.device.deviceName}")
        } catch (e: IOException) {
            Log.e(TAG, "Connection failed", e)
            disconnect()
            listener.onConnectionStateChanged(ConnectionState.ERROR)
            listener.onError("Connection failed: ${e.message}")
        }
    }

    fun disconnect() {
        ioManager?.apply {
            listener = null
            stop()
        }
        ioManager = null

        try {
            usbSerialPort?.close()
        } catch (e: IOException) {
            Log.w(TAG, "Error closing port", e)
        }
        usbSerialPort = null

        usbConnection?.close()
        usbConnection = null

        listener.onConnectionStateChanged(ConnectionState.DISCONNECTED)
    }

    fun write(data: ByteArray) {
        usbSerialPort?.let { port ->
            try {
                port.write(data, WRITE_TIMEOUT_MS)
            } catch (e: IOException) {
                Log.e(TAG, "Write failed", e)
                listener.onError("Write failed: ${e.message}")
            }
        }
    }

    fun isConnected(): Boolean = usbSerialPort != null

    companion object {
        private const val TAG = "UsbSerialManager"
        private const val BAUD_RATE = 115200
        private const val DATA_BITS = 8
        private const val WRITE_TIMEOUT_MS = 1000
    }
}
