/*
    LibrePods - AirPods liberated from Apple’s ecosystem
    Copyright (C) 2025 LibrePods contributors

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <https://www.gnu.org/licenses/>.
*/

@file:OptIn(ExperimentalEncodingApi::class)

package me.kavishdevar.librepods

import android.annotation.SuppressLint
import android.content.BroadcastReceiver
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.content.ServiceConnection
import android.media.AudioManager
import android.os.Build
import android.os.Bundle
import android.os.IBinder
import android.util.Log
import android.view.Gravity
import android.view.WindowManager
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.animation.Crossfade
import androidx.compose.animation.animateContentSize
import androidx.compose.animation.core.Animatable
import androidx.compose.animation.core.FastOutSlowInEasing
import androidx.compose.animation.core.Spring
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.spring
import androidx.compose.animation.core.tween
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.gestures.detectVerticalDragGestures
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableFloatStateOf
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import kotlinx.coroutines.launch
import me.kavishdevar.librepods.presentation.components.AdaptiveRainbowBrush
import me.kavishdevar.librepods.presentation.components.ControlCenterNoiseControlSegmentedButton
import me.kavishdevar.librepods.presentation.components.IconAreaSize
import me.kavishdevar.librepods.presentation.components.VerticalVolumeSlider
import me.kavishdevar.librepods.data.AirPodsNotifications
import me.kavishdevar.librepods.data.NoiseControlMode
import me.kavishdevar.librepods.services.AirPodsService
import me.kavishdevar.librepods.presentation.theme.LibrePodsTheme
import me.kavishdevar.librepods.bluetooth.AACPManager
import kotlin.io.encoding.ExperimentalEncodingApi
import kotlin.math.abs

class QuickSettingsDialogActivity : ComponentActivity() {

    private var airPodsService: AirPodsService? = null
    private var isBound = false

    private var isNoiseControlExpandedState by mutableStateOf(false)

    private val connection = object : ServiceConnection {
        override fun onServiceConnected(className: ComponentName, service: IBinder) {
            val binder = service as AirPodsService.LocalBinder
            airPodsService = binder.getService()
            isBound = true
            Log.d("QSActivity", "Service bound")
            setContent {
                LibrePodsTheme {
                    DraggableDismissBox(
                        onDismiss = { finish() },
                        onlyCollapseWhenClicked = {
                            if (isNoiseControlExpandedState) {
                                isNoiseControlExpandedState = false
                                true
                            } else {
                                false
                            }
                        }
                    ) {
                        if (isBound && airPodsService != null) {
                            NewControlCenterDialogContent(
                                service = airPodsService,
                                isNoiseControlExpanded = isNoiseControlExpandedState,
                                onNoiseControlExpandedChange = { isNoiseControlExpandedState = it }
                            )
                        }
                    }
                }
            }
        }

        override fun onServiceDisconnected(arg0: ComponentName) {
            isBound = false
            airPodsService = null
            Log.d("QSActivity", "Service unbound")
            finish()
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        window.addFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL)
        window.addFlags(WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH)
        window.addFlags(WindowManager.LayoutParams.FLAG_BLUR_BEHIND)
        window.setGravity(Gravity.BOTTOM)

        Intent(this, AirPodsService::class.java).also { intent ->
            bindService(intent, connection, BIND_AUTO_CREATE)
        }

        setContent {
            LibrePodsTheme {
                DraggableDismissBox(
                    onDismiss = { finish() },
                    onlyCollapseWhenClicked = {
                        if (isNoiseControlExpandedState) {
                            isNoiseControlExpandedState = false
                            true
                        } else {
                            false
                        }
                    }
                ) {
                    if (isBound && airPodsService != null) {
                        NewControlCenterDialogContent(
                            service = airPodsService,
                            isNoiseControlExpanded = isNoiseControlExpandedState,
                            onNoiseControlExpandedChange = { isNoiseControlExpandedState = it }
                        )
                    }
                }
            }
        }
    }

    override fun onDestroy() {
        super.onDestroy()
        if (isBound) {
            unbindService(connection)
            isBound = false
        }
    }
}

@Composable
fun DraggableDismissBox(
    onDismiss: () -> Unit,
    onlyCollapseWhenClicked: () -> Boolean,
    content: @Composable () -> Unit
) {
    val coroutineScope = rememberCoroutineScope()

    var dragOffset by remember { mutableFloatStateOf(0f) }
    var isDragging by remember { mutableStateOf(false) }
    val dismissThreshold = 400f

    val animatedOffset = remember { Animatable(0f) }
    val animatedScale = remember { Animatable(1f) }
    val animatedAlpha = remember { Animatable(1f) }

    val backgroundAlpha by animateFloatAsState(
        targetValue = if (isDragging) {
            val dragProgress = (abs(dragOffset) / 800f).coerceIn(0f, 0.8f)
            1f - dragProgress
        } else 1f,
        label = "BackgroundFade"
    )

    LaunchedEffect(isDragging) {
        if (!isDragging) {
            if (abs(dragOffset) < dismissThreshold) {
                val springSpec = spring(
                    dampingRatio = Spring.DampingRatioLowBouncy,
                    stiffness = Spring.StiffnessHigh,
                    visibilityThreshold = 0.1f
                )
                launch { animatedOffset.animateTo(0f, springSpec) }
                launch { animatedScale.animateTo(1f, springSpec) }
                launch { animatedAlpha.animateTo(1f, tween(100)) }
                dragOffset = 0f
            }
        }
    }

    LaunchedEffect(dragOffset, isDragging) {
        if (isDragging) {
            val dragProgress = (abs(dragOffset) / 1000f).coerceIn(0f, 0.5f)

            animatedOffset.snapTo(dragOffset)
            animatedScale.snapTo(1f - dragProgress * 0.3f)
            animatedAlpha.snapTo(1f - dragProgress * 0.7f)
        }
    }

    Box(
        modifier = Modifier
            .fillMaxSize()
            .background(Color.Black.copy(alpha = 0.5f * backgroundAlpha))
            .pointerInput(Unit) {
                detectVerticalDragGestures(
                    onDragStart = { isDragging = true },
                    onDragEnd = {
                        isDragging = false
                        if (abs(dragOffset) > dismissThreshold) {
                            coroutineScope.launch {
                                val direction = if (dragOffset > 0) 1f else -1f

                                launch {
                                    animatedOffset.animateTo(
                                        direction * 1500f,
                                        tween(350, easing = FastOutSlowInEasing)
                                    )
                                }
                                launch { animatedScale.animateTo(0.7f, tween(350)) }
                                launch { animatedAlpha.animateTo(0f, tween(250)) }

                                kotlinx.coroutines.delay(350)
                                onDismiss()
                            }
                        }
                    },
                    onDragCancel = { isDragging = false },
                    onVerticalDrag = { change, dragAmount ->
                        change.consume()
                        dragOffset += dragAmount
                    }
                )
            }
            .clickable(
                interactionSource = remember { MutableInteractionSource() },
                indication = null
            ) {
                onlyCollapseWhenClicked()
            },
        contentAlignment = Alignment.BottomCenter
    ) {
        Box(
            modifier = Modifier
                .fillMaxWidth()
                .graphicsLayer(
                    translationY = animatedOffset.value,
                    scaleX = animatedScale.value,
                    scaleY = animatedScale.value,
                    alpha = animatedAlpha.value
                ),
            contentAlignment = Alignment.BottomCenter
        ) {
            content()
        }
    }
}

@SuppressLint("UnspecifiedRegisterReceiverFlag")
@Composable
fun NewControlCenterDialogContent(
    service: AirPodsService?,
    isNoiseControlExpanded: Boolean,
    onNoiseControlExpandedChange: (Boolean) -> Unit
) {
    val context = LocalContext.current
    val sharedPreferences = context.getSharedPreferences("settings", Context.MODE_PRIVATE)
    val textColor = Color.White

    var currentAncMode by remember { mutableStateOf(NoiseControlMode.TRANSPARENCY) }
    var isConvAwarenessEnabled by remember { mutableStateOf(false) }

    val isOffModeEnabled = remember { sharedPreferences.getBoolean("off_listening_mode", true) }
    val availableModes = remember(isOffModeEnabled) {
        mutableListOf(
            NoiseControlMode.TRANSPARENCY,
            NoiseControlMode.ADAPTIVE,
            NoiseControlMode.NOISE_CANCELLATION
        ).apply {
            if (isOffModeEnabled) {
                add(0, NoiseControlMode.OFF)
            }
        }
    }

    val audioManager = context.getSystemService(Context.AUDIO_SERVICE) as AudioManager
    val maxVolume = remember { audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC) }
    var currentVolumeInt by remember { mutableIntStateOf(audioManager.getStreamVolume(AudioManager.STREAM_MUSIC)) }
    val animatedVolumeFraction by animateFloatAsState(
        targetValue = currentVolumeInt.toFloat() / maxVolume.toFloat(),
        animationSpec = spring(
            dampingRatio = Spring.DampingRatioLowBouncy,
            stiffness = Spring.StiffnessMediumLow
        ),
        label = "VolumeAnimation"
    )
    var liveDragFraction by remember { mutableFloatStateOf(animatedVolumeFraction) }
    var isDraggingVolume by remember { mutableStateOf(false) }
    LaunchedEffect(animatedVolumeFraction, isDraggingVolume) {
        if (!isDraggingVolume) {
            liveDragFraction = animatedVolumeFraction
        }
    }

    DisposableEffect(service, availableModes) {
        val ancReceiver = object : BroadcastReceiver() {
            override fun onReceive(context: Context, intent: Intent) {
                if (intent.action == AirPodsNotifications.ANC_DATA && service != null) {
                    val newModeOrdinal = intent.getIntExtra("data", NoiseControlMode.TRANSPARENCY.ordinal + 1) - 1
                    val newMode = NoiseControlMode.entries.getOrElse(newModeOrdinal) { NoiseControlMode.TRANSPARENCY }
                    if (availableModes.contains(newMode)) {
                         currentAncMode = newMode
                    } else if (newMode == NoiseControlMode.OFF && !isOffModeEnabled) {
                        currentAncMode = NoiseControlMode.TRANSPARENCY
                    }
                    Log.d("QSActivity", "ANC Receiver updated mode to: $currentAncMode (available: ${availableModes.joinToString()})")
                }
            }
        }
        val filter = IntentFilter(AirPodsNotifications.ANC_DATA)
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
            context.registerReceiver(ancReceiver, filter, Context.RECEIVER_EXPORTED)
        } else {
            context.registerReceiver(ancReceiver, filter)
        }

        service?.let {
            val initialModeOrdinal = it.getANC().minus(1)
            var initialMode = NoiseControlMode.entries.getOrElse(initialModeOrdinal) { NoiseControlMode.TRANSPARENCY }
            if (!availableModes.contains(initialMode)) {
                initialMode = NoiseControlMode.TRANSPARENCY
            }
            currentAncMode = initialMode
            isConvAwarenessEnabled = sharedPreferences.getBoolean("conversational_awareness", true)
            Log.d("QSActivity", "Initial ANC: $currentAncMode, ConvAware: $isConvAwarenessEnabled")
        }

        onDispose {
            context.unregisterReceiver(ancReceiver)
        }
    }

    DisposableEffect(Unit) {
        val volumeReceiver = object : BroadcastReceiver() {
            override fun onReceive(context: Context, intent: Intent) {
                if (intent.action == "android.media.VOLUME_CHANGED_ACTION") {
                    val newVolume = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC)
                    if (newVolume != currentVolumeInt) {
                        currentVolumeInt = newVolume
                        Log.d("QSActivity", "Volume Receiver updated volume to: $currentVolumeInt")
                    }
                }
            }
        }
        val filter = IntentFilter("android.media.VOLUME_CHANGED_ACTION")
        context.registerReceiver(volumeReceiver, filter)
        onDispose {
            context.unregisterReceiver(volumeReceiver)
        }
    }

    val deviceName = remember { sharedPreferences.getString("name", "AirPods") ?: "AirPods" }

    Column(
        modifier = Modifier
            .fillMaxSize()
            .background(Color.Transparent)
            .padding(horizontal = 24.dp)
            .pointerInput(Unit) {
                awaitPointerEventScope {
                    while (true) {
                        awaitPointerEvent()
                    }
                }
            },
        horizontalAlignment = Alignment.CenterHorizontally,
        verticalArrangement = Arrangement.SpaceBetween
    ) {
        if (service != null) {
            Spacer(modifier = Modifier.weight(1f))

            Column(
                modifier = Modifier
                    .weight(2f)
                    .fillMaxWidth(),
                horizontalAlignment = Alignment.CenterHorizontally,
                verticalArrangement = Arrangement.Center
            ) {
                Icon(
                    painter = painterResource(id = R.drawable.airpods),
                    contentDescription = "Device Icon",
                    tint = textColor.copy(alpha = 0.8f),
                    modifier = Modifier.size(48.dp)
                )

                Spacer(modifier = Modifier.height(4.dp))

                Text(
                    text = deviceName,
                    color = textColor,
                    fontSize = 16.sp,
                    fontWeight = FontWeight.Medium
                )

                Spacer(modifier = Modifier.height(32.dp))

                VerticalVolumeSlider(
                    displayFraction = animatedVolumeFraction,
                    maxVolume = maxVolume,
                    onVolumeChange = { newVolume ->
                        currentVolumeInt = newVolume
                        try {
                            audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, newVolume, 0)
                        } catch (e: Exception) { Log.e("QSActivity", "Failed to set volume", e) }
                    },
                    initialFraction = animatedVolumeFraction,
                    onDragStateChange = { dragging -> isDraggingVolume = dragging },
                    baseSliderHeight = 400.dp,
                    baseSliderWidth = 145.dp,
                    baseCornerRadius = 48.dp,
                    maxStretchFactor = 1.15f,
                    minCompressionFactor = 0.875f,
                    stretchSensitivity = 0.3f,
                    compressionSensitivity = 0.3f,
                    cornerRadiusChangeFactor = -0.5f,
                    directionalStretchRatio = 0.75f,
                    modifier = Modifier
                        .width(145.dp)
                        .padding(vertical = 8.dp)
                )
            }

            Spacer(modifier = Modifier.weight(1f))

            Box(
                modifier = Modifier
                    .fillMaxWidth()
                    .padding(bottom = 72.dp)
                    .animateContentSize(
                        animationSpec = spring(
                            dampingRatio = Spring.DampingRatioMediumBouncy,
                            stiffness = Spring.StiffnessMedium
                        )
                    ),
                contentAlignment = Alignment.Center
            ) {
                Crossfade(
                    targetState = isNoiseControlExpanded,
                    animationSpec = tween(durationMillis = 300),
                    label = "NoiseControlCrossfade"
                ) { expanded ->
                    if (expanded) {
                        ControlCenterNoiseControlSegmentedButton(
                            availableModes = availableModes,
                            selectedMode = currentAncMode,
                            onModeSelected = { newMode ->
                                service.aacpManager.sendControlCommand(
                                    identifier = AACPManager.Companion.ControlCommandIdentifiers.LISTENING_MODE.value,
                                    value = newMode.ordinal + 1
                                )
                                currentAncMode = newMode
                            },
                            modifier = Modifier.fillMaxWidth(0.8f)
                        )
                    } else {
                        Row(
                            modifier = Modifier.fillMaxWidth(0.85f),
                            horizontalArrangement = Arrangement.SpaceBetween,
                            verticalAlignment = Alignment.Top
                        ) {
                            val noiseControlButtonBrush = if (currentAncMode == NoiseControlMode.ADAPTIVE) {
                                AdaptiveRainbowBrush
                            } else {
                                null
                            }

                            Column(
                                horizontalAlignment = Alignment.CenterHorizontally,
                                verticalArrangement = Arrangement.Center,
                                modifier = Modifier.weight(1f)
                            ) {
                                Box(
                                    modifier = Modifier
                                        .size(IconAreaSize)
                                        .clip(CircleShape)
                                        .background(
                                            brush = noiseControlButtonBrush ?:
                                                Brush.linearGradient(colors = listOf(Color(0xFF0A84FF), Color(0xFF0A84FF)))
                                        )
                                        .clickable(
                                            onClick = { onNoiseControlExpandedChange(true) },
                                            indication = null,
                                            interactionSource = remember { MutableInteractionSource() }
                                        ),
                                    contentAlignment = Alignment.Center
                                ) {
                                    Icon(
                                        painter = painterResource(id = getModeIconRes(currentAncMode)),
                                        contentDescription = getModeLabel(currentAncMode),
                                        tint = Color.White,
                                        modifier = Modifier.size(32.dp)
                                    )
                                }

                                Spacer(modifier = Modifier.height(8.dp))

                                Text(
                                    text = getModeLabel(currentAncMode),
                                    color = Color.White,
                                    fontSize = 12.sp,
                                    fontWeight = FontWeight.Medium,
                                    textAlign = androidx.compose.ui.text.style.TextAlign.Center
                                )
                            }

                            Spacer(modifier = Modifier.width(24.dp))

                            Column(
                                horizontalAlignment = Alignment.CenterHorizontally,
                                verticalArrangement = Arrangement.Center,
                                modifier = Modifier.weight(1f)
                            ) {
                                Box(
                                    modifier = Modifier
                                        .size(IconAreaSize)
                                        .clip(CircleShape)
                                        .background(
                                            Brush.linearGradient(
                                                colors = listOf(
                                                    if (isConvAwarenessEnabled) Color(0xFF0A84FF) else Color(0x593C3C3E),
                                                    if (isConvAwarenessEnabled) Color(0xFF0A84FF) else Color(0x593C3C3E)
                                                )
                                            )
                                        )
                                        .clickable(
                                            onClick = {
                                                val newState = !isConvAwarenessEnabled
                                                service.aacpManager.sendControlCommand(
                                                    identifier = AACPManager.Companion.ControlCommandIdentifiers.CONVERSATION_DETECT_CONFIG.value,
                                                    value = newState
                                                )
                                                isConvAwarenessEnabled = newState
                                            },
                                            indication = null,
                                            interactionSource = remember { MutableInteractionSource() }
                                        ),
                                    contentAlignment = Alignment.Center
                                ) {
                                    Icon(
                                        painter = painterResource(id = R.drawable.airpods),
                                        contentDescription = "Conversational Awareness",
                                        tint = Color.White,
                                        modifier = Modifier.size(32.dp)
                                    )
                                }

                                Spacer(modifier = Modifier.height(8.dp))

                                Text(
                                    text = "Conversational\nAwareness",
                                    color = Color.White,
                                    fontSize = 12.sp,
                                    fontWeight = FontWeight.Medium,
                                    textAlign = androidx.compose.ui.text.style.TextAlign.Center,
                                    lineHeight = 14.sp
                                )
                            }
                        }
                    }
                }
            }

        } else {
            Spacer(modifier = Modifier.weight(1f))
            Box(modifier = Modifier.fillMaxWidth(), contentAlignment = Alignment.Center) {
                Text("Loading...", color = textColor)
            }
            Spacer(modifier = Modifier.weight(1f))
        }
    }
}

private fun getModeIconRes(mode: NoiseControlMode): Int {
    return when (mode) {
        NoiseControlMode.OFF -> R.drawable.noise_cancellation
        NoiseControlMode.TRANSPARENCY -> R.drawable.transparency
        NoiseControlMode.ADAPTIVE -> R.drawable.adaptive
        NoiseControlMode.NOISE_CANCELLATION -> R.drawable.noise_cancellation
    }
}

private fun getModeLabel(mode: NoiseControlMode): String {
    return when (mode) {
        NoiseControlMode.OFF -> "Off"
        NoiseControlMode.TRANSPARENCY -> "Transparency"
        NoiseControlMode.ADAPTIVE -> "Adaptive"
        NoiseControlMode.NOISE_CANCELLATION -> "Noise Cancel"
    }
}
