package com.manalejandro.odb2bluetooth.data.bluetooth

import android.annotation.SuppressLint
import android.bluetooth.BluetoothAdapter
import android.bluetooth.BluetoothDevice
import android.bluetooth.BluetoothSocket
import android.util.Log
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.withContext
import java.io.IOException
import java.io.InputStream
import java.io.OutputStream
import java.util.UUID

/**
 * Service to manage Bluetooth connection with ELM327 OBD2 adapter
 */
class BluetoothService {

    private var bluetoothSocket: BluetoothSocket? = null
    private var inputStream: InputStream? = null
    private var outputStream: OutputStream? = null

    private val _connectionState = MutableStateFlow(ConnectionState.DISCONNECTED)
    val connectionState: StateFlow<ConnectionState> = _connectionState

    private val _lastResponse = MutableStateFlow("")
    val lastResponse: StateFlow<String> = _lastResponse

    companion object {
        private const val TAG = "BluetoothService"
        // Standard SPP UUID for Bluetooth Serial
        private val SPP_UUID: UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB")
    }

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

    /**
     * Get list of paired Bluetooth devices
     */
    @SuppressLint("MissingPermission")
    fun getPairedDevices(): List<BluetoothDevice> {
        val bluetoothAdapter = BluetoothAdapter.getDefaultAdapter()
        return bluetoothAdapter?.bondedDevices?.toList() ?: emptyList()
    }

    /**
     * Connect to a Bluetooth device
     */
    @SuppressLint("MissingPermission")
    suspend fun connect(device: BluetoothDevice): Result<Unit> = withContext(Dispatchers.IO) {
        try {
            _connectionState.value = ConnectionState.CONNECTING

            // Close any existing connection
            disconnect()

            // Create socket and connect
            bluetoothSocket = device.createRfcommSocketToServiceRecord(SPP_UUID)
            bluetoothSocket?.connect()

            inputStream = bluetoothSocket?.inputStream
            outputStream = bluetoothSocket?.outputStream

            // Initialize ELM327
            initializeElm327()

            _connectionState.value = ConnectionState.CONNECTED
            Log.d(TAG, "Connected to ${device.name}")
            Result.success(Unit)
        } catch (e: Exception) {
            Log.e(TAG, "Connection error", e)
            _connectionState.value = ConnectionState.ERROR
            disconnect()
            Result.failure(e)
        }
    }

    /**
     * Initialize ELM327 with default commands
     */
    private suspend fun initializeElm327() {
        // Reset device
        sendCommand("ATZ")
        kotlinx.coroutines.delay(1000)

        // Turn off echo
        sendCommand("ATE0")
        kotlinx.coroutines.delay(100)

        // Set line feed off
        sendCommand("ATL0")
        kotlinx.coroutines.delay(100)

        // Set spaces off
        sendCommand("ATS0")
        kotlinx.coroutines.delay(100)

        // Set protocol to automatic
        sendCommand("ATSP0")
        kotlinx.coroutines.delay(100)
    }

    /**
     * Send a command to the ELM327 device
     */
    suspend fun sendCommand(command: String): Result<String> = withContext(Dispatchers.IO) {
        if (_connectionState.value != ConnectionState.CONNECTED) {
            return@withContext Result.failure(IOException("Not connected"))
        }

        try {
            val commandWithCR = "$command\r"
            outputStream?.write(commandWithCR.toByteArray())
            outputStream?.flush()

            Log.d(TAG, "Sent: $command")

            // Read response
            val response = readResponse()
            _lastResponse.value = response

            Log.d(TAG, "Received: $response")
            Result.success(response)
        } catch (e: Exception) {
            Log.e(TAG, "Send command error", e)
            Result.failure(e)
        }
    }

    /**
     * Read response from ELM327
     */
    private fun readResponse(): String {
        val buffer = ByteArray(1024)
        val response = StringBuilder()
        var bytesRead: Int

        try {
            val startTime = System.currentTimeMillis()
            val timeout = 5000 // 5 seconds timeout

            while (System.currentTimeMillis() - startTime < timeout) {
                if (inputStream?.available() ?: 0 > 0) {
                    bytesRead = inputStream?.read(buffer) ?: 0
                    if (bytesRead > 0) {
                        val chunk = String(buffer, 0, bytesRead)
                        response.append(chunk)

                        // Check for prompt character '>'
                        if (chunk.contains('>')) {
                            break
                        }
                    }
                }
                Thread.sleep(10)
            }
        } catch (e: Exception) {
            Log.e(TAG, "Read response error", e)
        }

        return response.toString()
            .replace(">", "")
            .replace("\r", "")
            .replace("\n", "")
            .trim()
    }

    /**
     * Disconnect from the Bluetooth device
     */
    fun disconnect() {
        try {
            inputStream?.close()
            outputStream?.close()
            bluetoothSocket?.close()
        } catch (e: Exception) {
            Log.e(TAG, "Disconnect error", e)
        } finally {
            inputStream = null
            outputStream = null
            bluetoothSocket = null
            _connectionState.value = ConnectionState.DISCONNECTED
        }
    }

    /**
     * Check if Bluetooth is available and enabled
     */
    fun isBluetoothAvailable(): Boolean {
        val bluetoothAdapter = BluetoothAdapter.getDefaultAdapter()
        return bluetoothAdapter != null && bluetoothAdapter.isEnabled
    }
}

