package com.erwan.revwatch

import android.Manifest
import android.content.BroadcastReceiver
import android.content.Intent
import android.content.pm.PackageManager
import android.graphics.Typeface
import android.os.Build
import android.os.Bundle
import android.view.Gravity
import android.view.HapticFeedbackConstants
import android.view.View
import android.widget.LinearLayout
import android.widget.TextView
import android.widget.Toast
import androidx.core.content.ContextCompat
import androidx.lifecycle.lifecycleScope
import com.erwan.revwatch.databinding.ActivitySetupBinding
import com.erwan.revwatch.model.BridgeConfig
import com.erwan.revwatch.model.ConnectionMode
import com.erwan.revwatch.model.DeviceItem
import com.erwan.revwatch.model.VehicleBrand
import com.erwan.revwatch.model.VehicleProfile
import com.erwan.revwatch.obd.ConnectionFactory
import com.erwan.revwatch.obd.DeviceScanner
import com.erwan.revwatch.obd.Elm327Client
import com.erwan.revwatch.obd.OpenTransportResult
import com.erwan.revwatch.prefs.AppPrefs
import com.erwan.revwatch.util.dp
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext

class SetupActivity : BaseActivity() {

    private lateinit var binding: ActivitySetupBinding
    private lateinit var deviceScanner: DeviceScanner

    private enum class SetupStep {
        CONNECTION_TYPE, DEVICE_SELECT, CONNECTION_TEST, BRAND_SELECT, MODEL_SELECT
    }

    private var currentStep = 0
    private var selectedMode: ConnectionMode = ConnectionMode.BLUETOOTH
    private var selectedDevice: DeviceItem? = null
    private var selectedBrand: VehicleBrand = VehicleBrand.FORD
    private var selectedVehicle: VehicleProfile = VehicleProfile.DEFAULT
    private var testPassed = false

    private var btDiscoveryReceiver: BroadcastReceiver? = null
    private val discoveredDevices = mutableListOf<DeviceItem>()
    private var isScanning = false

    private val deviceViewTags = mutableListOf<Pair<View, DeviceItem>>()
    private val brandViewTags = mutableListOf<Pair<View, VehicleBrand>>()
    private val modelViewTags = mutableListOf<Pair<View, VehicleProfile>>()

    private lateinit var modeViews: Map<View, ConnectionMode>

    private fun visibleSteps(): List<SetupStep> {
        return if (selectedMode == ConnectionMode.TEST) {
            listOf(SetupStep.CONNECTION_TYPE, SetupStep.BRAND_SELECT, SetupStep.MODEL_SELECT)
        } else {
            SetupStep.entries.toList()
        }
    }

    private fun currentSetupStep(): SetupStep = visibleSteps()[currentStep]

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivitySetupBinding.inflate(layoutInflater)
        setContentView(binding.root)
        enableImmersiveMode()

        deviceScanner = DeviceScanner(this)

        val initial = AppPrefs.load(this)
        selectedMode = initial.connectionMode
        selectedVehicle = initial.vehicleProfile
        selectedBrand = selectedVehicle.brand

        initModeGrid()

        binding.backButton.setOnClickListener {
            it.performHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK)
            onBack()
        }

        binding.nextButton.setOnClickListener {
            it.performHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK)
            onNext()
        }

        binding.retryButton.setOnClickListener {
            it.performHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK)
            runAutoConnectionTest()
        }

        renderStep()
    }

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

    // ──────── Grille de modes (step 0) ────────

    private fun initModeGrid() {
        modeViews = mapOf(
            binding.modeBluetooth to ConnectionMode.BLUETOOTH,
            binding.modeTest to ConnectionMode.TEST
        )

        modeViews.forEach { (view, mode) ->
            view.setOnClickListener {
                it.performHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK)
                selectedMode = mode
                selectedDevice = null
                testPassed = false
                updateModeSelection()
            }
        }
        updateModeSelection()
    }

    private fun updateModeSelection() {
        modeViews.forEach { (view, mode) ->
            view.isSelected = mode == selectedMode
        }
    }

    // ──────── Rendu des steps ────────

    private fun renderStep() {
        val steps = visibleSteps()
        if (currentStep >= steps.size) currentStep = steps.lastIndex

        binding.dotsContainer.removeAllViews()
        for (i in steps.indices) {
            val dot = View(this).apply {
                layoutParams = LinearLayout.LayoutParams(dp(5), dp(5)).apply {
                    if (i > 0) marginStart = dp(6)
                }
                setBackgroundResource(
                    if (i == currentStep) R.drawable.bg_dot_indicator_active
                    else R.drawable.bg_dot_indicator
                )
            }
            binding.dotsContainer.addView(dot)
        }

        val stepToView = mapOf(
            SetupStep.CONNECTION_TYPE to binding.stepConnectionType,
            SetupStep.DEVICE_SELECT to binding.stepDeviceSelect,
            SetupStep.CONNECTION_TEST to binding.stepConnectionTest,
            SetupStep.BRAND_SELECT to binding.stepBrandSelect,
            SetupStep.MODEL_SELECT to binding.stepModelSelect
        )

        val activeStep = steps[currentStep]

        stepToView.forEach { (step, view) ->
            if (step == activeStep) {
                view.visibility = View.VISIBLE
                view.alpha = 0f
                view.translationX = dp(16).toFloat()
                view.animate().alpha(1f).translationX(0f).setDuration(200L).start()
            } else {
                view.animate().cancel()
                view.visibility = View.GONE
                view.alpha = 1f
                view.translationX = 0f
            }
        }

        binding.nextButton.setImageResource(
            if (currentStep == steps.lastIndex) R.drawable.ic_check else R.drawable.ic_arrow_forward
        )
        showButton(binding.backButton, currentStep > 0)
        updateNavButtons()

        when (activeStep) {
            SetupStep.DEVICE_SELECT -> startDeviceScanning()
            SetupStep.CONNECTION_TEST -> runAutoConnectionTest()
            SetupStep.BRAND_SELECT -> refreshBrandList()
            SetupStep.MODEL_SELECT -> refreshModelList()
            else -> {}
        }
    }

    // ──────── Navigation ────────

    private fun canAdvance(): Boolean = when (currentSetupStep()) {
        SetupStep.CONNECTION_TYPE -> true
        SetupStep.DEVICE_SELECT -> selectedDevice != null
        SetupStep.CONNECTION_TEST -> testPassed
        SetupStep.BRAND_SELECT -> true
        SetupStep.MODEL_SELECT -> true
    }

    fun updateNavButtons() {
        showButton(binding.nextButton, canAdvance())
    }

    private fun showButton(btn: View, show: Boolean) {
        val isVisible = btn.visibility == View.VISIBLE && btn.alpha > 0.5f
        if (show && !isVisible) {
            btn.visibility = View.VISIBLE
            btn.alpha = 0f
            btn.scaleX = 0.5f
            btn.scaleY = 0.5f
            btn.animate().alpha(1f).scaleX(1f).scaleY(1f).setDuration(200L).start()
        } else if (!show && isVisible) {
            btn.animate().alpha(0f).scaleX(0.5f).scaleY(0.5f).setDuration(150L)
                .withEndAction { btn.visibility = View.GONE }.start()
        } else if (show) {
            btn.visibility = View.VISIBLE
            btn.alpha = 1f
            btn.scaleX = 1f
            btn.scaleY = 1f
        } else {
            btn.visibility = View.GONE
        }
    }

    private fun onNext() {
        when (currentSetupStep()) {
            SetupStep.CONNECTION_TYPE -> { currentStep++; renderStep() }
            SetupStep.DEVICE_SELECT -> {
                if (selectedDevice == null) return
                stopDeviceScanning()
                currentStep++
                renderStep()
            }
            SetupStep.CONNECTION_TEST -> {
                if (!testPassed) return
                currentStep++
                renderStep()
            }
            SetupStep.BRAND_SELECT -> { currentStep++; renderStep() }
            SetupStep.MODEL_SELECT -> saveAndFinish()
        }
    }

    private fun onBack() {
        if (currentStep > 0) {
            if (currentSetupStep() == SetupStep.DEVICE_SELECT) stopDeviceScanning()
            currentStep--
            renderStep()
        } else {
            binding.root.performHapticFeedback(HapticFeedbackConstants.REJECT)
            Toast.makeText(this, "Terminez la configuration d'abord", Toast.LENGTH_SHORT).show()
        }
    }

    // ──────── Scan Bluetooth (step 1) ────────

    private fun startDeviceScanning() {
        discoveredDevices.clear()
        deviceViewTags.clear()
        binding.deviceList.removeAllViews()
        isScanning = true
        binding.scanStatus.text = getString(R.string.setup_device_scanning)
        binding.scanStatus.setTextColor(ContextCompat.getColor(this, R.color.watch_muted))
        binding.scanStatus.visibility = View.VISIBLE

        scanBluetooth()
    }

    private fun stopDeviceScanning() {
        isScanning = false
        btDiscoveryReceiver?.let {
            deviceScanner.stopBluetoothDiscovery(it)
            btDiscoveryReceiver = null
        }
    }

    private fun scanBluetooth() {
        if (Build.VERSION.SDK_INT >= 31) {
            val hasConnect = ContextCompat.checkSelfPermission(
                this, Manifest.permission.BLUETOOTH_CONNECT
            ) == PackageManager.PERMISSION_GRANTED
            val hasScan = ContextCompat.checkSelfPermission(
                this, Manifest.permission.BLUETOOTH_SCAN
            ) == PackageManager.PERMISSION_GRANTED
            if (!hasConnect || !hasScan) {
                requestPermissions(
                    arrayOf(Manifest.permission.BLUETOOTH_CONNECT, Manifest.permission.BLUETOOTH_SCAN),
                    REQ_BLUETOOTH
                )
                binding.scanStatus.text = getString(R.string.setup_bt_permission_needed)
                isScanning = false
                return
            }
        }

        val bonded = deviceScanner.getBondedBluetoothDevices()
        if (bonded.isNotEmpty()) {
            addSectionLabel(getString(R.string.setup_device_paired))
            bonded.forEach { addDeviceItem(it) }
        }

        addSectionLabel(getString(R.string.setup_device_discovered))
        btDiscoveryReceiver = deviceScanner.startBluetoothDiscovery { device ->
            if (discoveredDevices.none { it.address == device.address }) {
                discoveredDevices.add(device)
                addDeviceItem(device)
            }
        }

        if (btDiscoveryReceiver == null && bonded.isEmpty()) {
            binding.scanStatus.text = getString(R.string.setup_device_none_found)
        } else {
            binding.scanStatus.text = getString(R.string.setup_device_scanning)
        }
        isScanning = false
    }

    private fun addSectionLabel(text: String) {
        val label = TextView(this).apply {
            this.text = text
            textSize = 9f
            setTextColor(ContextCompat.getColor(context, R.color.watch_muted))
            gravity = Gravity.START
            layoutParams = LinearLayout.LayoutParams(
                LinearLayout.LayoutParams.MATCH_PARENT,
                LinearLayout.LayoutParams.WRAP_CONTENT
            ).apply {
                topMargin = dp(6)
                bottomMargin = dp(2)
            }
        }
        binding.deviceList.addView(label)
    }

    private fun addDeviceItem(device: DeviceItem) {
        val item = LinearLayout(this).apply {
            orientation = LinearLayout.VERTICAL
            gravity = Gravity.CENTER
            layoutParams = LinearLayout.LayoutParams(
                LinearLayout.LayoutParams.MATCH_PARENT, dp(44)
            ).apply { bottomMargin = dp(4) }
            setBackgroundResource(R.drawable.bg_vehicle_option)
            setPadding(dp(10), dp(4), dp(10), dp(4))

            val nameView = TextView(context).apply {
                text = device.name
                textSize = 11f
                setTextColor(ContextCompat.getColor(context, R.color.watch_text))
                setTypeface(typeface, Typeface.BOLD)
                gravity = Gravity.CENTER
                layoutParams = LinearLayout.LayoutParams(
                    LinearLayout.LayoutParams.MATCH_PARENT,
                    LinearLayout.LayoutParams.WRAP_CONTENT
                )
            }
            addView(nameView)

            if (device.subtitle.isNotBlank()) {
                val subView = TextView(context).apply {
                    text = device.subtitle
                    textSize = 8f
                    setTextColor(ContextCompat.getColor(context, R.color.watch_muted))
                    gravity = Gravity.CENTER
                    layoutParams = LinearLayout.LayoutParams(
                        LinearLayout.LayoutParams.MATCH_PARENT,
                        LinearLayout.LayoutParams.WRAP_CONTENT
                    )
                }
                addView(subView)
            }

            setOnClickListener {
                it.performHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK)
                selectedDevice = device
                updateDeviceSelection()
                updateNavButtons()
            }
        }

        binding.deviceList.addView(item)
        deviceViewTags.add(item to device)

        if (selectedDevice == null) selectedDevice = device
        updateDeviceSelection()
        updateNavButtons()
    }

    private fun updateDeviceSelection() {
        deviceViewTags.forEach { (view, device) ->
            view.isSelected = device == selectedDevice
        }
    }

    // ──────── Test de connexion (step 2) ────────

    private fun runAutoConnectionTest() {
        testPassed = false
        binding.testStepsContainer.removeAllViews()
        binding.testProgress.visibility = View.VISIBLE
        binding.testResultLabel.text = ""
        binding.retryButton.visibility = View.GONE
        updateNavButtons()

        val phase1 = addTestPhase(getString(R.string.setup_test_opening))
        val phase2 = addTestPhase(getString(R.string.setup_test_init_elm))

        lifecycleScope.launch(Dispatchers.IO) {
            withContext(Dispatchers.Main) { setPhaseRunning(phase1) }

            val config = buildConfigFromDevice()
            val result = ConnectionFactory.openTransport(this@SetupActivity, config)

            when (result) {
                is OpenTransportResult.Ready -> {
                    withContext(Dispatchers.Main) { setPhaseSuccess(phase1) }

                    withContext(Dispatchers.Main) { setPhaseRunning(phase2) }
                    val elm = Elm327Client(result.transport)
                    val ok = try { elm.initialize() } catch (_: Exception) { false }
                    elm.close()

                    withContext(Dispatchers.Main) {
                        if (ok) {
                            setPhaseSuccess(phase2)
                            testPassed = true
                            binding.testProgress.visibility = View.GONE
                            binding.testResultLabel.text = getString(R.string.setup_test_success)
                            binding.testResultLabel.setTextColor(
                                ContextCompat.getColor(this@SetupActivity, R.color.watch_success)
                            )
                            updateNavButtons()
                            binding.root.performHapticFeedback(HapticFeedbackConstants.CONFIRM)
                            binding.root.postDelayed({
                                if (testPassed && currentSetupStep() == SetupStep.CONNECTION_TEST) {
                                    onNext()
                                }
                            }, 800)
                        } else {
                            setPhaseError(phase2, getString(R.string.setup_test_failed_handshake))
                            showTestFailure()
                        }
                    }
                }
                is OpenTransportResult.Error -> {
                    withContext(Dispatchers.Main) {
                        setPhaseError(phase1, result.message)
                        showTestFailure()
                    }
                }
            }
        }
    }

    private fun showTestFailure() {
        binding.testProgress.visibility = View.GONE
        binding.testResultLabel.text = getString(R.string.setup_test_failed)
        binding.testResultLabel.setTextColor(ContextCompat.getColor(this, R.color.watch_error))
        binding.retryButton.visibility = View.VISIBLE
        updateNavButtons()
        binding.root.performHapticFeedback(HapticFeedbackConstants.REJECT)
    }

    private fun addTestPhase(label: String): TextView {
        val tv = TextView(this).apply {
            text = "  $label"
            textSize = 10f
            setTextColor(ContextCompat.getColor(context, R.color.watch_muted))
            gravity = Gravity.START
            layoutParams = LinearLayout.LayoutParams(
                LinearLayout.LayoutParams.MATCH_PARENT,
                LinearLayout.LayoutParams.WRAP_CONTENT
            ).apply { bottomMargin = dp(3) }
            setCompoundDrawablesRelativeWithIntrinsicBounds(0, 0, 0, 0)
        }
        binding.testStepsContainer.addView(tv)
        return tv
    }

    private fun setPhaseRunning(tv: TextView) {
        tv.setTextColor(ContextCompat.getColor(this, R.color.watch_text))
    }

    private fun setPhaseSuccess(tv: TextView) {
        tv.setTextColor(ContextCompat.getColor(this, R.color.watch_success))
        tv.text = tv.text.toString().replaceFirst("  ", "  ✓ ")
    }

    private fun setPhaseError(tv: TextView, detail: String) {
        tv.setTextColor(ContextCompat.getColor(this, R.color.watch_error))
        tv.text = tv.text.toString().replaceFirst("  ", "  ✗ ") + "\n     $detail"
    }

    // ──────── Sélection marque (step 3) ────────

    private fun refreshBrandList() {
        binding.brandList.removeAllViews()
        brandViewTags.clear()

        val brands = VehicleBrand.entries.sortedBy { it.displayName }

        brands.forEach { brand ->
            val item = TextView(this).apply {
                text = brand.displayName
                textSize = 12f
                setTextColor(ContextCompat.getColor(context, R.color.watch_text))
                gravity = Gravity.CENTER
                textAlignment = View.TEXT_ALIGNMENT_CENTER
                layoutParams = LinearLayout.LayoutParams(
                    LinearLayout.LayoutParams.MATCH_PARENT, dp(38)
                ).apply { bottomMargin = dp(4) }
                setBackgroundResource(R.drawable.bg_vehicle_option)
                setTypeface(typeface, Typeface.BOLD)
                setPadding(dp(8), 0, dp(8), 0)

                setOnClickListener {
                    it.performHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK)
                    selectedBrand = brand
                    updateBrandSelection()
                }
            }
            binding.brandList.addView(item)
            brandViewTags.add(item to brand)
        }

        updateBrandSelection()

        val letters = brands.map { it.displayName.first().uppercaseChar() }.distinct()
        binding.brandAlphabet.setLetters(letters)
        binding.brandAlphabet.onLetterSelected = { letter ->
            val index = brands.indexOfFirst { it.displayName.first().uppercaseChar() == letter }
            if (index >= 0) binding.brandScrollView.smoothScrollTo(0, index * dp(42))
        }
    }

    private fun updateBrandSelection() {
        brandViewTags.forEach { (view, brand) ->
            view.isSelected = brand == selectedBrand
        }
    }

    // ──────── Sélection modèle (step 4) ────────

    private fun refreshModelList() {
        binding.modelList.removeAllViews()
        modelViewTags.clear()

        val models = VehicleProfile.forBrand(selectedBrand)

        if (selectedVehicle.brand != selectedBrand) {
            selectedVehicle = models.firstOrNull() ?: VehicleProfile.DEFAULT
        }

        models.forEach { profile ->
            val item = TextView(this).apply {
                text = profile.displayName
                textSize = 10f
                setTextColor(ContextCompat.getColor(context, R.color.watch_text))
                gravity = Gravity.CENTER
                layoutParams = LinearLayout.LayoutParams(
                    LinearLayout.LayoutParams.MATCH_PARENT, dp(38)
                ).apply { bottomMargin = dp(4) }
                setBackgroundResource(R.drawable.bg_vehicle_option)
                setPadding(dp(8), 0, dp(8), 0)

                setOnClickListener {
                    it.performHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK)
                    selectedVehicle = profile
                    updateModelSelection()
                }
            }
            binding.modelList.addView(item)
            modelViewTags.add(item to profile)
        }

        updateModelSelection()

        val letters = models.map { it.displayName.first().uppercaseChar() }.distinct()
        binding.modelAlphabet.setLetters(letters)
        binding.modelAlphabet.onLetterSelected = { letter ->
            val index = models.indexOfFirst { it.displayName.first().uppercaseChar() == letter }
            if (index >= 0) binding.modelScrollView.smoothScrollTo(0, index * dp(42))
        }
    }

    private fun updateModelSelection() {
        val models = VehicleProfile.forBrand(selectedBrand)
        modelViewTags.forEachIndexed { index, (view, _) ->
            view.isSelected = models.getOrNull(index) == selectedVehicle
        }
    }

    // ──────── Construction config ────────

    private fun buildConfigFromDevice(): BridgeConfig {
        val currentConfig = AppPrefs.load(this)
        val btMac = selectedDevice?.address.orEmpty()
        return currentConfig.copy(
            connectionMode = selectedMode,
            vehicleProfile = selectedVehicle,
            btMacAddress = btMac,
            setupComplete = false
        )
    }

    private fun saveAndFinish() {
        val config = buildConfigFromDevice().copy(
            vehicleProfile = selectedVehicle,
            setupComplete = true
        )
        AppPrefs.save(this, config)
        binding.root.performHapticFeedback(HapticFeedbackConstants.CONFIRM)
        startActivity(Intent(this, DashboardActivity::class.java))
        finish()
    }

    // ──────── Permissions ────────

    override fun onRequestPermissionsResult(
        requestCode: Int, permissions: Array<out String>, grantResults: IntArray
    ) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
        if (requestCode == REQ_BLUETOOTH) {
            val allGranted = grantResults.isNotEmpty() &&
                    grantResults.all { it == PackageManager.PERMISSION_GRANTED }
            if (allGranted && currentSetupStep() == SetupStep.DEVICE_SELECT) {
                startDeviceScanning()
            } else {
                binding.scanStatus.text = getString(R.string.setup_bt_permission_denied)
                binding.scanStatus.setTextColor(ContextCompat.getColor(this, R.color.watch_error))
            }
        }
    }

    @Deprecated("Use OnBackPressedCallback")
    override fun onBackPressed() {
        onBack()
    }

    companion object {
        private const val REQ_BLUETOOTH = 501
    }
}
