package com.obdwidgets.widget

import android.appwidget.AppWidgetManager
import android.appwidget.AppWidgetProvider
import android.content.Context
import android.content.Intent
import android.graphics.*
import android.util.TypedValue
import android.widget.RemoteViews
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.model.WidgetConfig
import com.obdwidgets.model.WidgetType
import com.obdwidgets.service.OBDService
import com.obdwidgets.ui.WidgetConfigurationActivity
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import kotlin.math.cos
import kotlin.math.sin
import kotlin.math.roundToInt

class OBDWidgetProvider : AppWidgetProvider() {
    
    override fun onUpdate(context: Context, appWidgetManager: AppWidgetManager, appWidgetIds: IntArray) {
        appWidgetIds.forEach { widgetId -> updateWidget(context, appWidgetManager, widgetId) }
    }
    
    override fun onReceive(context: Context, intent: Intent) {
        super.onReceive(context, intent)
        if (intent.action == AppWidgetManager.ACTION_APPWIDGET_UPDATE || intent.action == OBDService.APPWIDGET_UPDATE) {
            val manager = AppWidgetManager.getInstance(context)
            val widgetIds = manager.getAppWidgetIds(android.content.ComponentName(context, OBDWidgetProvider::class.java))
            onUpdate(context, manager, widgetIds)
        }
    }
    
    override fun onDeleted(context: Context, appWidgetIds: IntArray) {
        val prefs = context.getSharedPreferences("widget_configs", Context.MODE_PRIVATE)
        val editor = prefs.edit()
        appWidgetIds.forEach { widgetId -> editor.remove("widget_$widgetId") }
        editor.apply()
    }
    
    private fun updateWidget(context: Context, appWidgetManager: AppWidgetManager, widgetId: Int) {
        try {
            val config = loadWidgetConfig(context, widgetId) ?: run {
                val defaultConfig = WidgetConfig.create(widgetId, WidgetType.ANALOG, OBDParameter.SPEED, 0f, 200f)
                saveWidgetConfig(context, widgetId, defaultConfig)
                defaultConfig
            }
            
            val views = when (config.getWidgetType()) {
                WidgetType.ANALOG -> createAnalogWidget(context, config)
                WidgetType.DIGITAL -> createDigitalWidget(context, config)
                WidgetType.BAR -> createBarWidget(context, config)
                WidgetType.GAUGE -> createGaugeWidget(context, config)
                WidgetType.CIRCULAR -> createCircularWidget(context, config)
            }
            
            val configIntent = Intent(context, WidgetConfigurationActivity::class.java).apply {
                putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, widgetId)
                putExtra("config", config)
            }
            val configPendingIntent = android.app.PendingIntent.getActivity(
                context, widgetId, configIntent,
                android.app.PendingIntent.FLAG_UPDATE_CURRENT or android.app.PendingIntent.FLAG_IMMUTABLE
            )
            views.setOnClickPendingIntent(R.id.widget_container, configPendingIntent)
            appWidgetManager.updateAppWidget(widgetId, views)
        } catch (e: Exception) {
            val errorViews = RemoteViews(context.packageName, R.layout.widget_obd_base)
            appWidgetManager.updateAppWidget(widgetId, errorViews)
        }
    }
    
    private fun createAnalogWidget(context: Context, config: WidgetConfig): RemoteViews {
        val views = RemoteViews(context.packageName, R.layout.widget_analog)
        val parameter = config.getParameter()
        val data = OBDDataManager.getData(parameter)
        val status = getConnectionStatus(context)
        
        val value = data?.value?.coerceIn(config.minValue, config.maxValue) ?: config.minValue
        val label = config.customLabel ?: parameter.displayName
        
        val bitmap = drawGauge(context, value, config, label, parameter.unit, status)
        views.setImageViewBitmap(R.id.widget_gauge_image, bitmap)
        
        return views
    }

    private fun drawGauge(context: Context, value: Float, config: WidgetConfig, label: String, unit: String, status: ConnectionStatus): Bitmap {
        val size = 512
        val bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888)
        val canvas = Canvas(bitmap)
        val paint = Paint(Paint.ANTI_ALIAS_FLAG)
        val center = size / 2f
        val radius = size * 0.45f

        val startAngle = 135f
        val sweepAngle = 270f
        val min = config.minValue
        val max = config.maxValue
        val range = max - min

        // 1. Background Circle
        paint.color = Color.parseColor("#E61E1E1E")
        paint.style = Paint.Style.FILL
        canvas.drawCircle(center, center, radius, paint)

        // 2. Outer ring stroke
        paint.color = Color.DKGRAY
        paint.style = Paint.Style.STROKE
        paint.strokeWidth = 8f
        canvas.drawCircle(center, center, radius, paint)

        // Step size logic
        val stepSize = when {
            range <= 10 -> 1f
            range <= 50 -> 5f
            range <= 100 -> 10f
            range <= 300 -> 20f
            range <= 1000 -> 100f
            else -> 1000f
        }
        val minorStepSize = stepSize / 5f
        val totalTicks = (range / minorStepSize).roundToInt()
        
        // 3. Draw Scale
        paint.color = config.labelColor
        paint.textSize = 28f
        paint.textAlign = Paint.Align.CENTER

        for (i in 0..totalTicks) {
            val currentVal = min + i * minorStepSize
            if (currentVal > max + 0.01f) break

            val angle = startAngle + ((currentVal - min) / range) * sweepAngle
            val rad = Math.toRadians(angle.toDouble())
            
            val isMajor = (i % 5 == 0)
            val tickLength = if (isMajor) 30f else if (i % 1 == 0) 15f else 0f
            if (tickLength == 0f) continue

            paint.style = Paint.Style.STROKE
            paint.strokeWidth = if (isMajor) 5f else 2f

            val xStart = (center + radius * cos(rad)).toFloat()
            val yStart = (center + radius * sin(rad)).toFloat()
            val xEnd = (center + (radius - tickLength) * cos(rad)).toFloat()
            val yEnd = (center + (radius - tickLength) * sin(rad)).toFloat()
            canvas.drawLine(xStart, yStart, xEnd, yEnd, paint)
            
            if (isMajor) {
                paint.style = Paint.Style.FILL
                val textR = radius - 65f
                val xText = (center + textR * cos(rad)).toFloat()
                val yText = (center + textR * sin(rad)).toFloat() + 10f
                canvas.drawText(currentVal.roundToInt().toString(), xText, yText, paint)
            }
        }

        // 4. Integrated Readout
        if (status == ConnectionStatus.CONNECTED) {
            // Value
            paint.style = Paint.Style.FILL
            paint.color = config.valueColor
            paint.textSize = config.valueFontSize.toFloat() * 1.5f // Scaled for 512px canvas
            paint.typeface = Typeface.DEFAULT_BOLD
            val valueStr = if (range > 50) value.roundToInt().toString() else String.format("%.1f", value)
            canvas.drawText(valueStr, center, center + radius * 0.5f, paint)

            // Unit
            paint.textSize = 28f
            paint.color = config.labelColor
            paint.typeface = Typeface.DEFAULT
            canvas.drawText(unit, center, center + radius * 0.65f, paint)

            // Label
            paint.textSize = 24f
            paint.color = config.labelColor
            paint.typeface = Typeface.DEFAULT_BOLD
            canvas.drawText(label.uppercase(), center, center + radius * 0.78f, paint)
        }

        // 5. Needle
        val currentAngle = startAngle + ((value - min) / range) * sweepAngle
        val needleRad = Math.toRadians(currentAngle.toDouble())
        paint.color = config.needleColor
        paint.style = Paint.Style.STROKE
        paint.strokeWidth = 10f
        paint.strokeCap = Paint.Cap.ROUND
        val needleLength = radius - 40f
        canvas.drawLine(center, center, (center + needleLength * cos(needleRad)).toFloat(), (center + needleLength * sin(needleRad)).toFloat(), paint)

        // 6. Center hub
        paint.style = Paint.Style.FILL
        paint.color = Color.DKGRAY
        canvas.drawCircle(center, center, 18f, paint)
        paint.color = config.needleColor
        canvas.drawCircle(center, center, 10f, paint)
        
        return bitmap
    }
    
    private fun createCircularWidget(context: Context, config: WidgetConfig): RemoteViews {
        val views = RemoteViews(context.packageName, R.layout.widget_analog)
        val parameter = config.getParameter()
        val data = OBDDataManager.getData(parameter)
        val status = getConnectionStatus(context)
        
        val value = data?.value?.coerceIn(config.minValue, config.maxValue) ?: config.minValue
        val label = config.customLabel ?: parameter.displayName
        
        val bitmap = drawCircularGauge(context, value, config, label, parameter.unit, status)
        views.setImageViewBitmap(R.id.widget_gauge_image, bitmap)
        
        return views
    }

    private fun drawCircularGauge(context: Context, value: Float, config: WidgetConfig, label: String, unit: String, status: ConnectionStatus): Bitmap {
        val size = 512
        val bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888)
        val canvas = Canvas(bitmap)
        val paint = Paint(Paint.ANTI_ALIAS_FLAG)
        val center = size / 2f
        
        // Use full available size for the bar
        val strokeWidth = 50f
        val radius = (size / 2f) - (strokeWidth / 2f) - 10f // Margin of 10px

        val startAngle = 135f
        val sweepAngle = 270f
        val min = config.minValue
        val max = config.maxValue
        val range = max - min

        // 1. Background Circle
        paint.color = Color.parseColor("#E61E1E1E")
        paint.style = Paint.Style.FILL
        canvas.drawCircle(center, center, size / 2f - 5f, paint)

        // 2. Background Track
        paint.style = Paint.Style.STROKE
        paint.strokeWidth = strokeWidth
        paint.color = Color.DKGRAY
        paint.strokeCap = Paint.Cap.ROUND
        val rect = RectF(center - radius, center - radius, center + radius, center + radius)
        canvas.drawArc(rect, startAngle, sweepAngle, false, paint)

        // 3. Progress Bar
        if (status == ConnectionStatus.CONNECTED) {
            val progressSweep = ((value - min) / range) * sweepAngle
            paint.color = config.needleColor 
            canvas.drawArc(rect, startAngle, progressSweep, false, paint)
        }

        // 4. Scale (Ticks and Numbers)
        paint.textAlign = Paint.Align.CENTER
        
        val stepSize = when {
            range <= 10 -> 1f
            range <= 50 -> 5f
            range <= 100 -> 10f
            range <= 300 -> 20f
            range <= 1000 -> 100f
            else -> 1000f
        }
        val minorStepSize = stepSize / 5f
        val totalTicks = (range / minorStepSize).roundToInt()

        for (i in 0..totalTicks) {
            val currentVal = min + i * minorStepSize
            if (currentVal > max + 0.01f) break

            val angle = startAngle + ((currentVal - min) / range) * sweepAngle
            val rad = Math.toRadians(angle.toDouble())
            
            val isMajor = (i % 5 == 0)
            
            // Draw tick marks inside the bar
            paint.style = Paint.Style.STROKE
            paint.strokeWidth = if (isMajor) 4f else 2f
            paint.color = if (isMajor) config.scaleColor else Color.DKGRAY
            
            // Ticks across the bar
            val tickInnerR = radius - (strokeWidth / 2f) + 5f
            val tickOuterR = radius + (strokeWidth / 2f) - 5f
            
            val xStart = (center + tickInnerR * cos(rad)).toFloat()
            val yStart = (center + tickInnerR * sin(rad)).toFloat()
            val xEnd = (center + tickOuterR * cos(rad)).toFloat()
            val yEnd = (center + tickOuterR * sin(rad)).toFloat()
            canvas.drawLine(xStart, yStart, xEnd, yEnd, paint)
            
            if (isMajor) {
                // Draw numbers slightly INSIDE the bar for better visibility and to avoid clipping
                paint.style = Paint.Style.FILL
                paint.color = config.scaleColor
                paint.textSize = 24f
                paint.typeface = Typeface.DEFAULT_BOLD
                
                val textR = radius - (strokeWidth / 2f) - 25f
                val xText = (center + textR * cos(rad)).toFloat()
                val yText = (center + textR * sin(rad)).toFloat() + 8f
                canvas.drawText(currentVal.roundToInt().toString(), xText, yText, paint)
            }
        }

        // 5. Central Readout
        paint.style = Paint.Style.FILL
        paint.textAlign = Paint.Align.CENTER
        
        // Value
        paint.color = config.valueColor
        paint.textSize = config.valueFontSize.toFloat() * 1.8f
        paint.typeface = Typeface.DEFAULT_BOLD
        val valueStr = if (range > 50) value.roundToInt().toString() else String.format("%.1f", value)
        canvas.drawText(valueStr, center, center + 30f, paint)

        // Unit
        paint.textSize = 32f
        paint.color = config.labelColor
        paint.typeface = Typeface.DEFAULT
        canvas.drawText(unit, center, center + radius * 0.45f, paint)

        // Label
        paint.textSize = 28f
        paint.color = config.labelColor
        paint.typeface = Typeface.DEFAULT_BOLD
        canvas.drawText(label.uppercase(), center, center + radius * 0.7f, paint)

        return bitmap
    }
    
    private fun createDigitalWidget(context: Context, config: WidgetConfig): RemoteViews {
        val views = RemoteViews(context.packageName, R.layout.widget_digital)
        val parameter = config.getParameter()
        val data = OBDDataManager.getData(parameter)
        val status = getConnectionStatus(context)
        
        views.setTextViewText(R.id.widget_label, config.customLabel ?: parameter.displayName)
        views.setTextColor(R.id.widget_label, config.labelColor)
        views.setTextColor(R.id.widget_unit, config.labelColor)
        
        views.setTextColor(R.id.widget_value, config.valueColor)
        views.setTextViewTextSize(R.id.widget_value, TypedValue.COMPLEX_UNIT_SP, config.valueFontSize.toFloat())
        
        if (data != null && status == ConnectionStatus.CONNECTED) {
            val value = data.value.coerceIn(config.minValue, config.maxValue)
            val format = if (parameter == OBDParameter.SPEED || parameter == OBDParameter.RPM) "%.0f" else "%.1f"
            views.setTextViewText(R.id.widget_value, String.format(format, value))
            views.setTextViewText(R.id.widget_unit, parameter.unit)
        } else {
            views.setTextViewText(R.id.widget_value, "--")
        }
        return views
    }
    
    private fun createBarWidget(context: Context, config: WidgetConfig): RemoteViews {
        val views = RemoteViews(context.packageName, R.layout.widget_bar)
        return views
    }
    
    private fun createGaugeWidget(context: Context, config: WidgetConfig): RemoteViews {
        return createAnalogWidget(context, config)
    }
    
    private fun getConnectionStatus(context: Context): ConnectionStatus {
        val prefs = PreferenceManager.getDefaultSharedPreferences(context)
        val statusStr = prefs.getString("last_connection_status", ConnectionStatus.DISCONNECTED.name)
        return try { ConnectionStatus.valueOf(statusStr ?: ConnectionStatus.DISCONNECTED.name) } catch (e: Exception) { ConnectionStatus.DISCONNECTED }
    }
    
    companion object {
        private val json = Json { ignoreUnknownKeys = true }
        fun loadWidgetConfig(context: Context, widgetId: Int): WidgetConfig? {
            val prefs = context.getSharedPreferences("widget_configs", Context.MODE_PRIVATE)
            val configJson = prefs.getString("widget_$widgetId", null) ?: return null
            return try { json.decodeFromString<WidgetConfig>(configJson) } catch (e: Exception) { null }
        }
        fun saveWidgetConfig(context: Context, widgetId: Int, config: WidgetConfig) {
            val prefs = context.getSharedPreferences("widget_configs", Context.MODE_PRIVATE)
            val configJson = json.encodeToString(config)
            prefs.edit().putString("widget_$widgetId", configJson).apply()
        }
    }
}
