package com.example.variableaxlemonitor.bluetooth

import android.annotation.SuppressLint
import android.bluetooth.*
import android.bluetooth.le.ScanCallback
import android.bluetooth.le.ScanResult
import android.content.Context
import android.os.Build
import android.os.Handler
import android.os.Looper
import android.util.Log
import android.widget.Toast
import com.example.variableaxlemonitor.data.AxleData
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import java.util.*

class BluetoothManager(private val context: Context) {
    private val TAG = "BluetoothManager"
    private val androidBluetoothManager = context.getSystemService(Context.BLUETOOTH_SERVICE) as android.bluetooth.BluetoothManager
    private val adapter: BluetoothAdapter? = androidBluetoothManager.adapter
    private val scanner = adapter?.bluetoothLeScanner
    
    private val scope = CoroutineScope(Dispatchers.IO + SupervisorJob())

    // ESP32 BLE UART Service & Characteristic UUIDs
    private val SERVICE_UUID = UUID.fromString("6E400001-B5A3-F393-E0A9-E50E24DCCA9E")
    private val RX_CHAR_UUID = UUID.fromString("6E400003-B5A3-F393-E0A9-E50E24DCCA9E")
    private val CLIENT_CONFIG_DESCRIPTOR = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb")

    private val _isConnected = MutableStateFlow(false)
    val isConnected: StateFlow<Boolean> = _isConnected

    private val _lastMessage = MutableStateFlow("기기 선택 대기 중")
    val lastMessage: StateFlow<String> = _lastMessage

    private val _mtu = MutableStateFlow(23)
    val mtu: StateFlow<Int> = _mtu

    private val _axleData = MutableStateFlow(AxleData())
    val axleData: StateFlow<AxleData> = _axleData

    private val _dataReceivedTrigger = MutableSharedFlow<Unit>()
    val dataReceivedTrigger: SharedFlow<Unit> = _dataReceivedTrigger

    private val _foundDevices = MutableStateFlow<List<BluetoothDevice>>(emptyList())
    val foundDevices: StateFlow<List<BluetoothDevice>> = _foundDevices

    private var gatt: BluetoothGatt? = null

    private val scanCallback = object : ScanCallback() {
        @SuppressLint("MissingPermission")
        override fun onScanResult(callbackType: Int, result: ScanResult) {
            val device = result.device
            if (device.name != null) {
                val currentList = _foundDevices.value
                if (currentList.none { it.address == device.address }) {
                    _foundDevices.value = currentList + device
                }
            }
        }
    }

    private val gattCallback = object : BluetoothGattCallback() {
        @SuppressLint("MissingPermission")
        override fun onConnectionStateChange(gatt: BluetoothGatt, status: Int, newState: Int) {
            Log.d(TAG, "onConnectionStateChange: status=$status, newState=$newState")
            if (status == BluetoothGatt.GATT_SUCCESS) {
                if (newState == BluetoothProfile.STATE_CONNECTED) {
                    _lastMessage.value = "GATT 연결 성공. MTU 요청 중..."
                    Log.d(TAG, "Connected to GATT server. Requesting MTU...")
                    Handler(Looper.getMainLooper()).post {
                        Toast.makeText(context, "블루투스 기기에 연결되었습니다.", Toast.LENGTH_SHORT).show()
                    }
                    gatt.requestMtu(512)
                } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
                    _lastMessage.value = "기기 연결 끊김"
                    Log.d(TAG, "Disconnected from GATT server.")
                    _isConnected.value = false
                    this@BluetoothManager.gatt?.close()
                    this@BluetoothManager.gatt = null
                }
            } else {
                _lastMessage.value = "연결 오류 (status=$status)"
                Log.e(TAG, "GATT error: status=$status")
                _isConnected.value = false
                gatt.close()
                this@BluetoothManager.gatt = null
            }
        }

        @SuppressLint("MissingPermission")
        override fun onMtuChanged(gatt: BluetoothGatt, mtu: Int, status: Int) {
            Log.d(TAG, "onMtuChanged: mtu=$mtu, status=$status")
            _mtu.value = mtu
            _lastMessage.value = "MTU 설정 완료 ($mtu). 서비스 검색 시작..."
            // Discover services after MTU is set
            gatt.discoverServices()
        }

        @SuppressLint("MissingPermission")
        override fun onServicesDiscovered(gatt: BluetoothGatt, status: Int) {
            Log.d(TAG, "onServicesDiscovered: status=$status")
            if (status == BluetoothGatt.GATT_SUCCESS) {
                _lastMessage.value = "서비스 발견 성공. 특성 찾는 중..."
                var foundTarget = false
                val service = gatt.getService(SERVICE_UUID)
                val characteristic = service?.getCharacteristic(RX_CHAR_UUID)
                
                if (characteristic != null) {
                    _lastMessage.value = "UART 특성 발견. 구독 설정 중..."
                    enableNotifications(gatt, characteristic)
                    foundTarget = true
                } else {
                    Log.w(TAG, "Target UART UUID not found. Searching for any Notify characteristic...")
                    for (srv in gatt.services) {
                        for (char in srv.characteristics) {
                            if ((char.properties and BluetoothGattCharacteristic.PROPERTY_NOTIFY) != 0) {
                                Log.d(TAG, "Found Notify characteristic: ${char.uuid} in service ${srv.uuid}")
                                _lastMessage.value = "대체 특성 발견. 구독 설정 중..."
                                enableNotifications(gatt, char)
                                foundTarget = true
                                break
                            }
                        }
                        if (foundTarget) break
                    }
                }
                
                if (foundTarget) {
                    _isConnected.value = true
                } else {
                    _lastMessage.value = "데이터 수신 특성을 찾을 수 없음"
                    Log.e(TAG, "No suitable Notify characteristic found on device.")
                }
            } else {
                _lastMessage.value = "서비스 발견 실패 (status=$status)"
            }
        }

        override fun onCharacteristicChanged(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic, value: ByteArray) {
            processReceivedData(value)
        }

        @Deprecated("Deprecated in Java")
        override fun onCharacteristicChanged(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic) {
            @Suppress("DEPRECATION")
            processReceivedData(characteristic.value)
        }


        //1. 역할: "구독 승인 확인"
            //BLE 기기(ESP32-C3)로부터 데이터를 자동으로 받으려면(Notification), 단순히 앱에서 "받겠다"고 설정하는 것뿐만 아니라,
            // **기기 내부의 스위치(Descriptor)**를 켜줘야 합니다.
            // onDescriptorWrite는 바로 그 스위치를 켜는 작업이 성공했는지 결과를 알려주는 시점입니다.
        //2. 동작 흐름 (Flow)
            // 1.서비스 발견: 기기의 기능을 찾습니다. (onServicesDiscovered)
            //2.특성 알림 설정: 앱 내부적으로 알림을 받을 준비를 합니다. (setCharacteristicNotification)
            //3.디스크립터 쓰기: 기기에게 "이제 데이터를 보내줘"라고 명령을 보냅니다. (writeDescriptor)
            //4.결과 수신 (onDescriptorWrite): 기기가 명령을 잘 받았는지 확인합니다. (현재 코드 부분)
            //5.데이터 수신: 이때부터 실제 데이터가 들어옵니다. (onCharacteristicChanged)

        override fun onDescriptorWrite(gatt: BluetoothGatt, descriptor: BluetoothGattDescriptor, status: Int) {
            // 1. 로그 기록: 어떤 결과(status)로 어떤 항목(descriptor)에 쓰기가 완료되었는지 남깁니다.
            Log.d(TAG, "onDescriptorWrite: status=$status, descriptor=${descriptor.uuid}")
            // 2. 성공 여부 판단: status가 0(GATT_SUCCESS)이면 성공입니다.
            if (status == BluetoothGatt.GATT_SUCCESS) {
                // 성공 시: 사용자가 볼 수 있게 상태 메시지를 업데이트합니다.
                // 이제부터 기기가 데이터를 보내기 시작할 준비가 완벽히 끝났음을 의미합니다.
                _lastMessage.value = "통신 준비 완료. 데이터 수신 대기 중..."
            } else {
                // 실패 시: 에러 코드와 함께 실패 메시지를 띄웁니다.
                // 이 경우 기기가 데이터를 보내지 않으므로 원인을 파악해야 합니다.
                _lastMessage.value = "데이터 수신 실패 (status=$status)"
            }
        }
    }

    private fun processReceivedData(value: ByteArray?) {
        if (value == null) return
        
        val received = String(value).trim()
        if (received.isNotEmpty()) {
            Log.d(TAG, "Extracted line: '$received'")
            scope.launch {
                parseData(received)
            }
        }
    }

    @SuppressLint("MissingPermission")
    private fun enableNotifications(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic) {
        val success = gatt.setCharacteristicNotification(characteristic, true)
        Log.d(TAG, "setCharacteristicNotification(${characteristic.uuid}) success: $success")
        
        val descriptor = characteristic.getDescriptor(CLIENT_CONFIG_DESCRIPTOR)
        if (descriptor != null) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
                gatt.writeDescriptor(descriptor, BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE)
            } else {
                @Suppress("DEPRECATION")
                descriptor.value = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE
                @Suppress("DEPRECATION")
                gatt.writeDescriptor(descriptor)
            }
            Log.d(TAG, "Writing Descriptor to enable Notifications for ${characteristic.uuid}")
        } else {
            Log.e(TAG, "Client Config Descriptor not found for ${characteristic.uuid}")
        }
    }

    @SuppressLint("MissingPermission")
    fun startScan() {
        Log.d(TAG, "Starting BLE scan...")
        _foundDevices.value = emptyList()
        scanner?.startScan(scanCallback)
    }

    @SuppressLint("MissingPermission")
    fun stopScan() {
        Log.d(TAG, "Stopping BLE scan.")
        scanner?.stopScan(scanCallback)
    }

    @SuppressLint("MissingPermission")
    fun connect(device: BluetoothDevice) {
        Log.d(TAG, "Connecting to ${device.address} via TRANSPORT_LE")
        stopScan()
        disconnect()
        gatt = device.connectGatt(context, false, gattCallback, BluetoothDevice.TRANSPORT_LE)
    }

    private suspend fun parseData(line: String) {
        // Remove any non-numeric/comma characters except dots and minus
        val cleaned = line.replace(Regex("[^0-9,.-]"), "")
        if (cleaned.isEmpty()) return
        Log.d(TAG, "Parsing Cleaned Line: '$cleaned'")
        
        val parts = cleaned.split(",").filter { it.isNotEmpty() }.map { it.trim() }
        if (parts.size >= 5) {
            try {
                val pRawStr = parts[0]
                val sRawStr = parts[1]
                val vRawStr = parts[2]
                val dRawStr = parts[3]
                val iRawStr = parts[4]
                
                Log.d(TAG, "Part Strings: P=$pRawStr, S=$sRawStr, V=$vRawStr, D=$dRawStr, I=$iRawStr")

                // Parse as Int first as requested by user
                val pInt = pRawStr.toIntOrNull() ?: 0
                val sInt = sRawStr.toIntOrNull() ?: 0
                val vInt = vRawStr.toIntOrNull() ?: 0
                val dInt = dRawStr.toIntOrNull() ?: 0
                val iInt = iRawStr.toIntOrNull() ?: 0

                val data = AxleData(
                    pressure = pInt,
                    speed = sInt,
                    variableAxleLoad = vInt,
                    driveAxleLoad = dInt,
                    isDown = iInt == 1,
                    timestamp = System.currentTimeMillis()
                )
                
                Log.d(TAG, "Successfully Parsed Raw Data: $data")
                
                // Use update block for better StateFlow awareness
                _axleData.update { data }
                
                // Update on Main thread to ensure UI recomposition
                withContext(Dispatchers.Main) {
                    _dataReceivedTrigger.emit(Unit)
                }
            } catch (e: Exception) {
                Log.e(TAG, "Parse error: ${e.message} in '$cleaned'")
            }
        } else {
            Log.w(TAG, "Incomplete data parts (${parts.size}): $cleaned")
        }
    }

    @SuppressLint("MissingPermission")
    fun disconnect() {
        Log.d(TAG, "Disconnecting GATT")
        gatt?.disconnect()
        _isConnected.value = false
    }
}
