package es.freetimelab.pluriwave

import android.Manifest
import android.content.Intent
import android.content.pm.PackageManager
import android.media.audiofx.Visualizer
import android.os.Build
import android.os.Handler
import android.os.Looper
import androidx.core.app.NotificationManagerCompat
import com.ryanheise.audioservice.AudioServiceActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.EventChannel
import io.flutter.plugin.common.MethodChannel

class MainActivity : AudioServiceActivity() {
    private val visualizerChannel = "pluriwave/audio_visualizer"
    private val alarmChannel = "pluriwave/alarm_scheduler"
    private val permissionRequestCode = 4821
    private var visualizer: Visualizer? = null
    private var pendingSink: EventChannel.EventSink? = null
    private var pendingArgs: Map<*, *>? = null
    private var alarmMethodChannel: MethodChannel? = null
    private val mainHandler = Handler(Looper.getMainLooper())

    override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
        super.configureFlutterEngine(flutterEngine)
        EventChannel(
            flutterEngine.dartExecutor.binaryMessenger,
            visualizerChannel
        ).setStreamHandler(object : EventChannel.StreamHandler {
            override fun onListen(arguments: Any?, events: EventChannel.EventSink?) {
                pendingSink = events
                pendingArgs = arguments as? Map<*, *>
                startVisualizerWhenAllowed()
            }

            override fun onCancel(arguments: Any?) {
                stopVisualizer()
                pendingSink = null
                pendingArgs = null
            }
        })

        val alarmScheduler = AlarmScheduler(this)
        alarmMethodChannel = MethodChannel(
            flutterEngine.dartExecutor.binaryMessenger,
            alarmChannel
        )
        alarmMethodChannel?.setMethodCallHandler { call, result ->
            when (call.method) {
                "scheduleAlarm" -> {
                    val id = call.argument<String>("id")
                    val title = call.argument<String>("title") ?: "PluriWave"
                    val triggerAtMillis = call.argument<Long>("triggerAtMillis")
                    val preNoticeAtMillis = call.argument<Long>("preNoticeAtMillis") ?: 0L
                    if (id == null || triggerAtMillis == null) {
                        result.error("INVALID_ALARM", "Missing alarm id or trigger time", null)
                    } else {
                        alarmScheduler.scheduleAlarm(id, title, triggerAtMillis, preNoticeAtMillis)
                        result.success(null)
                    }
                }
                "cancelAlarm" -> {
                    val id = call.argument<String>("id")
                    if (id == null) {
                        result.error("INVALID_ALARM", "Missing alarm id", null)
                    } else {
                        alarmScheduler.cancelAlarm(id)
                        result.success(null)
                    }
                }
                "dismissAlarmNotification" -> {
                    val id = call.argument<String>("id")
                    if (id == null) {
                        result.error("INVALID_ALARM", "Missing alarm id", null)
                    } else {
                        alarmScheduler.dismissFireNotification(id)
                        result.success(null)
                    }
                }
                "diagnostics" -> {
                    result.success(
                        mapOf(
                            "canScheduleExactAlarms" to alarmScheduler.canScheduleExactAlarms(),
                            "notificationsEnabled" to NotificationManagerCompat.from(this).areNotificationsEnabled(),
                            "manufacturer" to Build.MANUFACTURER,
                            "sdkInt" to Build.VERSION.SDK_INT
                        )
                    )
                }
                "getInitialAlarmIntent" -> {
                    result.success(alarmPayload(intent))
                    intent?.removeExtra(PluriWaveAlarmReceiver.EXTRA_ALARM_ACTION)
                }
                else -> result.notImplemented()
            }
        }
    }

    override fun onNewIntent(intent: Intent) {
        super.onNewIntent(intent)
        setIntent(intent)
        val payload = alarmPayload(intent)
        if (payload.isNotEmpty()) {
            alarmMethodChannel?.invokeMethod("alarmFired", payload)
        }
    }

    private fun alarmPayload(intent: Intent?): Map<String, Any> {
        if (intent == null) return emptyMap()
        val action = intent.getStringExtra(PluriWaveAlarmReceiver.EXTRA_ALARM_ACTION)
            ?: return emptyMap()
        val alarmId = intent.getStringExtra(PluriWaveAlarmReceiver.EXTRA_ALARM_ID)
            ?: return emptyMap()
        val title = intent.getStringExtra(PluriWaveAlarmReceiver.EXTRA_ALARM_TITLE)
            ?: "PluriWave"
        return mapOf(
            "alarmId" to alarmId,
            "alarmTitle" to title,
            "alarmAction" to action
        )
    }

    private fun startVisualizerWhenAllowed() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M &&
            checkSelfPermission(Manifest.permission.RECORD_AUDIO)
            != PackageManager.PERMISSION_GRANTED
        ) {
            requestPermissions(
                arrayOf(Manifest.permission.RECORD_AUDIO),
                permissionRequestCode
            )
            return
        }
        startVisualizer()
    }

    private fun startVisualizer() {
        val sink = pendingSink ?: return
        val args = pendingArgs
        val sessionId = (args?.get("sessionId") as? Number)?.toInt() ?: 0
        val bands = ((args?.get("bands") as? Number)?.toInt() ?: 26).coerceIn(8, 96)

        stopVisualizer()

        try {
            val captureSize = Visualizer.getCaptureSizeRange()[1]
            visualizer = Visualizer(sessionId).apply {
                enabled = false
                setCaptureSize(captureSize)
                setDataCaptureListener(
                    object : Visualizer.OnDataCaptureListener {
                        override fun onWaveFormDataCapture(
                            visualizer: Visualizer?,
                            waveform: ByteArray?,
                            samplingRate: Int
                        ) {
                            val data = waveform ?: return
                            val values = downsample(data, bands)
                            mainHandler.post { sink.success(values) }
                        }

                        override fun onFftDataCapture(
                            visualizer: Visualizer?,
                            fft: ByteArray?,
                            samplingRate: Int
                        ) = Unit
                    },
                    Visualizer.getMaxCaptureRate() / 2,
                    true,
                    false
                )
                enabled = true
            }
        } catch (error: Throwable) {
            sink.error("VISUALIZER_UNAVAILABLE", error.message, null)
            stopVisualizer()
        }
    }

    private fun downsample(data: ByteArray, bands: Int): List<Double> {
        if (data.isEmpty()) return emptyList()
        val bucket = maxOf(1, data.size / bands)
        val values = ArrayList<Double>(bands)

        var index = 0
        while (index < data.size && values.size < bands) {
            var sum = 0.0
            var count = 0
            val end = minOf(index + bucket, data.size)
            for (i in index until end) {
                val centered = (data[i].toInt() and 0xFF) - 128
                sum += kotlin.math.abs(centered) / 128.0
                count++
            }
            values.add(if (count == 0) 0.0 else (sum / count).coerceIn(0.0, 1.0))
            index = end
        }

        while (values.size < bands) values.add(0.0)
        return values
    }

    private fun stopVisualizer() {
        try {
            visualizer?.enabled = false
            visualizer?.release()
        } catch (_: Throwable) {
        } finally {
            visualizer = null
        }
    }

    override fun onRequestPermissionsResult(
        requestCode: Int,
        permissions: Array<out String>,
        grantResults: IntArray
    ) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
        if (requestCode != permissionRequestCode) return

        if (grantResults.firstOrNull() == PackageManager.PERMISSION_GRANTED) {
            startVisualizer()
        } else {
            pendingSink?.error(
                "RECORD_AUDIO_DENIED",
                "Permiso de audio denegado para visualizar la onda real",
                null
            )
        }
    }

    override fun onDestroy() {
        stopVisualizer()
        super.onDestroy()
    }
}
