package com.obdwidgets.service

import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.bluetooth.BluetoothAdapter
import android.bluetooth.BluetoothDevice
import android.bluetooth.BluetoothManager
import android.content.Context
import android.content.Intent
import android.content.SharedPreferences
import android.os.Binder
import android.os.Build
import android.os.IBinder
import androidx.core.app.NotificationCompat
import androidx.lifecycle.LifecycleService
import androidx.lifecycle.lifecycleScope
import androidx.preference.PreferenceManager
import com.obdwidgets.R
import com.obdwidgets.model.ConnectionStatus
import com.obdwidgets.model.OBDDataManager
import com.obdwidgets.model.OBDParameter
import com.obdwidgets.obd.ELM327Protocol
import com.obdwidgets.obd.OBDProtocol
import com.obdwidgets.ui.SettingsActivity
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import java.util.concurrent.atomic.AtomicReference

/**
 * Foreground Service für OBD-Verbindung und Datenabfrage
 */
class OBDService : LifecycleService() {
    
    private val binder = LocalBinder()
    private var protocol: OBDProtocol? = null
    private var connectionStatus = AtomicReference(ConnectionStatus.DISCONNECTED)
    private var dataUpdateJob: Job? = null
    private var reconnectJob: Job? = null
    private var isTestMode = false
    
    private val prefs: SharedPreferences by lazy {
        PreferenceManager.getDefaultSharedPreferences(this)
    }
    
    inner class LocalBinder : Binder() {
        fun getService(): OBDService = this@OBDService
    }
    
    override fun onCreate() {
        super.onCreate()
        createNotificationChannel()
        startForeground(NOTIFICATION_ID, createNotification())
        checkModeAndStart()
    }
    
    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        checkModeAndStart()
        return super.onStartCommand(intent, flags, startId)
    }

    private fun checkModeAndStart() {
        isTestMode = prefs.getBoolean("test_mode", false)
        if (isTestMode) {
            startTestMode()
        } else {
            startConnection()
        }
    }
    
    override fun onBind(intent: Intent): IBinder {
        super.onBind(intent)
        return binder
    }
    
    override fun onDestroy() {
        super.onDestroy()
        stopConnection()
    }
    
    private fun startConnection() {
        lifecycleScope.launch {
            val deviceAddress = prefs.getString("bt_device_address", null)
            if (deviceAddress == null) {
                if (!isTestMode) {
                    updateStatus(ConnectionStatus.ERROR)
                }
                return@launch
            }
            
            try {
                val bluetoothManager = getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
                val bluetoothAdapter = bluetoothManager.adapter
                if (bluetoothAdapter == null) {
                    updateStatus(ConnectionStatus.ERROR)
                    return@launch
                }
                val device = bluetoothAdapter.getRemoteDevice(deviceAddress)
                connectToDevice(device)
            } catch (e: Exception) {
                updateStatus(ConnectionStatus.ERROR)
            }
        }
    }
    
    private suspend fun connectToDevice(device: BluetoothDevice) {
        if (connectionStatus.get() == ConnectionStatus.CONNECTING) return
        updateStatus(ConnectionStatus.CONNECTING)
        
        protocol = ELM327Protocol(device)
        val connected = protocol?.initialize() ?: false
        
        if (connected) {
            updateStatus(ConnectionStatus.CONNECTED)
            sendConnectionBroadcast(true)
            startDataUpdates()
        } else {
            updateStatus(ConnectionStatus.ERROR)
            scheduleReconnect()
        }
    }
    
    private fun startDataUpdates() {
        dataUpdateJob?.cancel()
        dataUpdateJob = lifecycleScope.launch {
            while (isActive) {
                if (isTestMode) {
                    updateTestData()
                    delay(100) // 10 Hz in test mode for smoothness
                } else {
                    updateOBDData()
                    // OBD is slower, but we can try to be faster than 1s
                    // Most Bluetooth OBD adapters manage 5-10 PIDs per second
                    delay(50) 
                }
            }
        }
    }
    
    private suspend fun updateOBDData() {
        val protocol = this@OBDService.protocol ?: return
        
        if (!protocol.isConnected()) {
            updateStatus(ConnectionStatus.DISCONNECTED)
            scheduleReconnect()
            return
        }
        
        // We query ALL parameters in one loop. 
        // For smoothness, we should probably only query what is actually on screen,
        // but for now let's just make the loop as fast as the adapter allows.
        var dataChanged = false
        OBDParameter.values().forEach { parameter ->
            try {
                val data = protocol.readPid(parameter.pid)
                if (data != null && data.isNotEmpty()) {
                    val value = parameter.formula(data)
                    OBDDataManager.updateData(parameter, value)
                    dataChanged = true
                }
            } catch (e: Exception) {
                // Ignore single parameter errors
            }
        }
        
        if (dataChanged) {
            updateWidgets()
        }
    }
    
    private suspend fun updateTestData() {
        OBDParameter.values().forEach { parameter ->
            val range = parameter.maxValue - parameter.minValue
            val time = System.currentTimeMillis()
            val value = when (parameter) {
                OBDParameter.SPEED -> {
                    // Smooth sine wave for speed
                    val sinValue = (Math.sin(time / 5000.0) + 1) / 2.0 // 0.0 to 1.0
                    (sinValue * 120).toFloat()
                }
                OBDParameter.RPM -> {
                    // Smooth RPM simulation
                    val sinValue = (Math.sin(time / 2000.0) + 1) / 2.0
                    (800f + sinValue * 4000f).toFloat()
                }
                else -> parameter.minValue + (time % (range.toLong() * 1000)) / 1000f
            }
            OBDDataManager.updateData(parameter, value.coerceIn(parameter.minValue, parameter.maxValue))
        }
        updateWidgets()
    }
    
    private fun updateWidgets() {
        val intent = Intent(APPWIDGET_UPDATE)
        intent.setPackage(packageName)
        sendBroadcast(intent)
    }
    
    private fun scheduleReconnect() {
        if (isTestMode) return
        
        reconnectJob?.cancel()
        val autoConnect = prefs.getBoolean("auto_connect", true)
        if (!autoConnect) return
        
        reconnectJob = lifecycleScope.launch {
            val intervalString = prefs.getString("reconnect_interval", "5") ?: "5"
            val interval = (intervalString.toIntOrNull() ?: 5) * 1000L
            delay(interval)
            
            if (connectionStatus.get() != ConnectionStatus.CONNECTED) {
                updateStatus(ConnectionStatus.RECONNECTING)
                startConnection()
            }
        }
    }
    
    private fun stopConnection() {
        dataUpdateJob?.cancel()
        reconnectJob?.cancel()
        
        lifecycleScope.launch {
            protocol?.disconnect()
            protocol = null
            updateStatus(ConnectionStatus.DISCONNECTED)
        }
    }
    
    private fun updateStatus(status: ConnectionStatus) {
        connectionStatus.set(status)
        prefs.edit().putString("last_connection_status", status.name).apply()
        updateNotification()
        
        val intent = Intent(ACTION_CONNECTION_STATUS_CHANGED)
        intent.putExtra(EXTRA_STATUS, status.name)
        sendBroadcast(intent)
    }

    private fun sendConnectionBroadcast(connected: Boolean) {
        val intent = Intent(if (connected) ACTION_CONNECTED else ACTION_DISCONNECTED)
        sendBroadcast(intent)
    }
    
    fun getConnectionStatus(): ConnectionStatus = connectionStatus.get()
    
    private fun createNotificationChannel() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            val channel = NotificationChannel(
                CHANNEL_ID,
                "OBD Service",
                NotificationManager.IMPORTANCE_LOW
            )
            val manager = getSystemService(NotificationManager::class.java)
            manager.createNotificationChannel(channel)
        }
    }
    
    private fun createNotification(): Notification {
        val intent = Intent(this, SettingsActivity::class.java)
        val pendingIntent = PendingIntent.getActivity(
            this, 0, intent,
            PendingIntent.FLAG_IMMUTABLE
        )
        
        val statusText = when (connectionStatus.get()) {
            ConnectionStatus.CONNECTED -> "Verbunden"
            ConnectionStatus.CONNECTING -> "Verbinde..."
            ConnectionStatus.INITIALIZING -> "Initialisiere..."
            ConnectionStatus.RECONNECTING -> "Wiederverbinde..."
            ConnectionStatus.DISCONNECTED -> "Getrennt"
            ConnectionStatus.ERROR -> "Fehler"
        }
        
        return NotificationCompat.Builder(this, CHANNEL_ID)
            .setContentTitle("OBD Widgets")
            .setContentText(statusText)
            .setSmallIcon(R.drawable.ic_notification)
            .setContentIntent(pendingIntent)
            .setOngoing(true)
            .build()
    }
    
    private fun updateNotification() {
        val notification = createNotification()
        val manager = getSystemService(NotificationManager::class.java)
        manager.notify(NOTIFICATION_ID, notification)
    }
    
    private fun startTestMode() {
        isTestMode = true
        updateStatus(ConnectionStatus.CONNECTED)
        startDataUpdates()
    }
    
    companion object {
        private const val CHANNEL_ID = "obd_service_channel"
        private const val NOTIFICATION_ID = 1
        
        const val ACTION_CONNECTION_STATUS_CHANGED = "com.obdwidgets.CONNECTION_STATUS_CHANGED"
        const val ACTION_CONNECTED = "com.obdwidgets.CONNECTED"
        const val ACTION_DISCONNECTED = "com.obdwidgets.DISCONNECTED"
        const val EXTRA_STATUS = "status"
        const val APPWIDGET_UPDATE = "android.appwidget.action.APPWIDGET_UPDATE"
    }
}
