package com.mybus17000.ui.screens


import android.Manifest
import android.annotation.SuppressLint
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.graphics.Canvas
import android.graphics.Paint
import android.os.Looper
import android.speech.RecognizerIntent
import android.util.Log
import android.widget.Toast
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.core.FastOutSlowInEasing
import androidx.compose.animation.core.LinearOutSlowInEasing
import androidx.compose.animation.core.animateDp
import androidx.compose.animation.core.animateFloat
import androidx.compose.animation.core.tween
import androidx.compose.animation.core.updateTransition
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.slideInVertically
import androidx.compose.animation.slideOutVertically
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
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.heightIn
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Delete
import androidx.compose.material.icons.filled.Directions
import androidx.compose.material.icons.filled.KeyboardArrowDown
import androidx.compose.material.icons.filled.Mic
import androidx.compose.material.icons.filled.MyLocation
import androidx.compose.material.icons.filled.Star
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.ModalBottomSheet
import androidx.compose.material3.Text
import androidx.compose.material3.rememberModalBottomSheetState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.mutableStateMapOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.runtime.snapshots.SnapshotStateList
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.core.graphics.createBitmap
import androidx.core.graphics.toColorInt
import com.google.android.gms.location.FusedLocationProviderClient
import com.google.android.gms.location.LocationCallback
import com.google.android.gms.location.LocationRequest
import com.google.android.gms.location.LocationResult
import com.google.android.gms.location.LocationServices
import com.google.android.gms.location.Priority
import com.google.android.gms.maps.CameraUpdateFactory
import com.google.android.gms.maps.MapsInitializer
import com.google.android.gms.maps.model.BitmapDescriptor
import com.google.android.gms.maps.model.BitmapDescriptorFactory
import com.google.android.gms.maps.model.CameraPosition
import com.google.android.gms.maps.model.LatLng
import com.google.android.gms.maps.model.LatLngBounds
import com.google.maps.android.compose.CameraPositionState
import com.google.maps.android.compose.GoogleMap
import com.google.maps.android.compose.MapProperties
import com.google.maps.android.compose.MapUiSettings
import com.google.maps.android.compose.MapsComposeExperimentalApi
import com.google.maps.android.compose.Marker
import com.google.maps.android.compose.MarkerInfoWindow
import com.google.maps.android.compose.Polyline
import com.google.maps.android.compose.rememberCameraPositionState
import com.google.maps.android.compose.rememberMarkerState
import com.mybus17000.R
import com.mybus17000.data.AddressOrStopSearchBar
import com.mybus17000.data.RoutePolylineBus
import com.mybus17000.data.RoutePolylineData
import com.mybus17000.data.StopFields
import com.mybus17000.data.fetchRoutePolylinesByTrip_id
import com.mybus17000.data.fetchStopPredictions
import com.mybus17000.data.fetchStopTimes
import com.mybus17000.data.fetchStops
import com.mybus17000.data.findBestRoute
import com.mybus17000.data.geocodeAddressSuspend
import com.mybus17000.data.getFavorites
import com.mybus17000.data.getNextTwoTimesWithLineName
import com.mybus17000.data.saveFavorites
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.util.Locale
import kotlin.coroutines.cancellation.CancellationException
import kotlin.math.abs
import com.mybus17000.data.findNearestStops
import com.mybus17000.data.loadAllBusRoutes
import com.mybus17000.data.loadPolylineDataFromAssets


@OptIn(MapsComposeExperimentalApi::class, ExperimentalMaterial3Api::class)
@Composable
fun BusMapScreen() {
    val context = LocalContext.current
    val coroutineScope = rememberCoroutineScope()

    // Etats principaux
    var mapLoaded by remember { mutableStateOf(false) }
    var userLocation by remember { mutableStateOf(LatLng(46.1603, -1.1511)) }

    val stops = remember { mutableStateOf<List<StopFields>>(emptyList()) }
    var addressQuery by remember { mutableStateOf("") }
    val selectedStop = remember { mutableStateOf<StopFields?>(null) }

    var showFavorites by remember { mutableStateOf(false) }
    val isLoading = remember { mutableStateOf(false) }

//    var searchQuery by remember { mutableStateOf("") }
//    val suggestions = remember { mutableStateListOf<String>() }
    val markers = remember { mutableStateListOf<LatLng>() }
    val favoriteStops = remember { mutableStateListOf<String>() }
    val geocodeCache = remember { mutableStateMapOf<String, LatLng>() }
    val routePolylines = remember { mutableStateOf<Map<String, RoutePolylineData>>(emptyMap()) }
    val addressSuggestions = remember { mutableStateListOf<String>() }

    val fusedClient = remember { LocationServices.getFusedLocationProviderClient(context) }

    val hasPermission = remember {
        mutableStateOf(
            ContextCompat.checkSelfPermission(
                context,
                Manifest.permission.ACCESS_FINE_LOCATION
            ) == PackageManager.PERMISSION_GRANTED
        )
    }

    val locationRequest = remember {
        LocationRequest.Builder(Priority.PRIORITY_HIGH_ACCURACY, 5000L)
            .setMinUpdateDistanceMeters(5f)
            .build()
    }

    val cameraState = rememberCameraPositionState {
        position = CameraPosition.fromLatLngZoom(userLocation, 16f)
    }

    // 🔹 Gère les callbacks de position
    val locationCallback = remember {
        object : LocationCallback() {
            override fun onLocationResult(result: LocationResult) {
                val location = result.lastLocation ?: return
                val latLng = LatLng(location.latitude, location.longitude)
                userLocation = latLng

                if (mapLoaded && cameraState.isMoving.not()) {
                    try {
                        cameraState.move(CameraUpdateFactory.newLatLngZoom(latLng, 15f))
                    } catch (_: CancellationException) { }
                }
            }
        }
    }
    val locationPermissionLauncher =
        rememberLauncherForActivityResult(ActivityResultContracts.RequestPermission()) { granted ->
            if (granted) {
                hasPermission.value = true
                startLocationUpdatesSafely(
                    context, fusedClient, locationRequest, locationCallback
                )
            } else {
                Toast.makeText(context, "Permission localisation refusée", Toast.LENGTH_SHORT).show()
            }
        }
    // ---------------- Speech Recognizer ----------------
    val speechLauncher = rememberLauncherForActivityResult(
        contract = ActivityResultContracts.StartActivityForResult()
    ) { result ->
        if (result.resultCode == Activity.RESULT_OK) {
            val spokenText = result.data
                ?.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS)
                ?.firstOrNull()
            spokenText?.let { addressQuery = it }
        }
    }
    var showRoutePanel by remember { mutableStateOf(false) }
    var routeStart by remember { mutableStateOf<LatLng?>(null) }
    var routeEnd by remember { mutableStateOf<LatLng?>(null) }
    val routePoints = remember { mutableStateOf<Map<String, RoutePolylineData>>(emptyMap()) }

    // 🔸 Sécurisation du lancement
    LaunchedEffect(mapLoaded, hasPermission.value) {
        if (!hasPermission.value) {
            locationPermissionLauncher.launch(Manifest.permission.ACCESS_FINE_LOCATION)
        } else if (mapLoaded) {
            coroutineScope.launch(Dispatchers.IO) {
                loadStopsAndFavorites(
                    context, stops, addressQuery, addressSuggestions,
                    favoriteStops, markers, geocodeCache, cameraState
                )
            }
            startLocationUpdatesSafely(
                context, fusedClient, locationRequest, locationCallback
            )
        }
    }

    // 🔹 Garde le state en dehors du if
    val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true)
    val routePolylinesBus = remember { mutableStateOf<Map<String, RoutePolylineBus>>(emptyMap()) }
    // 🔹 Liste brute des polylines (données graphiques)
    val routePolylinesData = remember { mutableStateOf<List<RoutePolylineData>>(emptyList()) }
//    routePolylinesData.value = loadPolylineDataFromAssets(context)
//
//    LaunchedEffect(routePolylinesData.value, stops.value) {
//        if (routePolylinesData.value.isNotEmpty() && stops.value.isNotEmpty()) {
//            routePolylinesBus.value = loadAllBusRoutes(
//                context = context,
//                routePolylineDataList = routePolylinesData.value,
//                stops = stops.value
//            )
//            Log.d("ROUTES_INIT", "✅ ${routePolylinesBus.value.size} lignes chargées")
//        }
//    }

    // -----------------------------------
    // UI
    // -----------------------------------
    Column {
        SearchBarSection(
            context = context,
            fusedClient = fusedClient,
            addressQuery = addressQuery,
            onQueryChange = { addressQuery = it },
            stops = stops.value,
            addressSuggestions = addressSuggestions,
            geocodeCache = geocodeCache,
            markers = markers,
            favoriteStops = favoriteStops,
            cameraState = cameraState,
            coroutineScope = coroutineScope,
            speechLauncher = speechLauncher
        )

        Box(Modifier.fillMaxSize()) {
            MapSection(
                hasPermission = hasPermission.value,
                cameraState = cameraState,
                routePolylines = routePolylines,
                stops = stops.value,
                markers = markers,
                isLoading =  isLoading ,
                selectedStop = selectedStop,
                context = context,
                coroutineScope = coroutineScope,
                onMapLoaded = { mapLoaded = true },
                modifier = Modifier
                    .align(Alignment.TopCenter)
                    .fillMaxWidth()
                    .background(Color.LightGray)
                    .padding(vertical = 8.dp),
                showRoutePanel = showRoutePanel,
                routeStart = routeStart,
                routeEnd = routeEnd ,
                routePoints = routePoints
            )

            FavoritesPanel(
                showFavorites = showFavorites,
                favoriteStops = favoriteStops,
                markers = markers,
                geocodeCache = geocodeCache,
                cameraState = cameraState,
                context = context,
                coroutineScope = coroutineScope,
                modifier = Modifier
                    .align(Alignment.BottomEnd)
                    .padding(vertical = 12.dp)
            )

            TopActionButtons(
                showFavorites = showFavorites,
                onToggleFavorites = { showFavorites = !showFavorites },
                onSpeechClick = { speechLauncher.launch(getSpeechIntent()) },
                onLocateClick = {
                    coroutineScope.launch {
                        centerOnUser(context, fusedClient, cameraState) }
                },
                modifier = Modifier
                    .align(Alignment.TopEnd)
                    .padding(vertical = 30.dp),
                showRoutePanel = showRoutePanel,
                onShowRoutePanelChange = { showRoutePanel = it }, // ✅ met à jour l’état
            )

            if (isLoading.value) LoadingDialog()

            val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true)

            val transition = updateTransition(targetState = showRoutePanel, label = "RoutePanelTransition")

            val alpha by transition.animateFloat(
                transitionSpec = { tween(durationMillis = 250, easing = LinearOutSlowInEasing) },
                label = "alpha"
            ) { if (it) 1f else 0f }

            val offsetY by transition.animateDp(
                transitionSpec = { tween(durationMillis = 250, easing = FastOutSlowInEasing) },
                label = "offsetY"
            ) { if (it) 0.dp else 60.dp }

            val density = LocalDensity.current
            val offsetYPx = with(density) { offsetY.toPx() }

            if (showRoutePanel || transition.currentState) {
                Box(
                    modifier = Modifier
                        .fillMaxSize()
                        .graphicsLayer {
                            this.alpha = alpha
                            this.translationY = offsetY.toPx()
                        }
                        .background(MaterialTheme.colorScheme.background.copy(alpha = 0.3f)) // flou visuel doux
                ) {
                    ModalBottomSheet(
                        onDismissRequest = { showRoutePanel = false },
                        sheetState = sheetState,
                        containerColor = MaterialTheme.colorScheme.surface,
                        tonalElevation = 8.dp
                    ) {
                        RouteSearchPanel(
                            stops = stops.value,
                            onValidate = { start, end ->
                                routeStart = start
                                routeEnd = end
                                showRoutePanel = false

                                coroutineScope.launch {
                                    // 🔹 Trouve les arrêts les plus proches
                                    val nearestStart = findNearestStops(start, stops.value).firstOrNull()?.first
                                    val nearestEnd = findNearestStops(end, stops.value).firstOrNull()?.first

                                    if (nearestStart == null || nearestEnd == null) {
                                        Toast.makeText(context, "Aucun arrêt trouvé à proximité", Toast.LENGTH_SHORT).show()
                                        return@launch
                                    }

                                    val startStopId = nearestStart.stop_name
                                    val endStopId = nearestEnd.stop_name

                                    // 🔹 Recherche du meilleur trajet
                                    val bestRoute = findBestRoute(startStopId, endStopId, routePolylinesBus.value)

                                    if (bestRoute != null) {
                                        // ✅ Construit la nouvelle Map pour affichage
                                        val newPolylines: Map<String, RoutePolylineBus> =
                                            bestRoute.segments.associate { segment ->
                                                segment.routeId to RoutePolylineBus(
                                                    stops = listOf(
                                                        segment.fromStop,
                                                        segment.toStop
                                                    ),
                                                    polyline = segment.polyline,
                                                    routeId = segment.routeId
                                                )
                                            }

                                        routePolylinesBus.value = newPolylines

                                        // 🔹 Recentre la carte sur l’ensemble du trajet
                                        val bounds = LatLngBounds.builder()
                                        bestRoute.segments.forEach { seg ->
                                            seg.polyline.points.forEach { bounds.include(it) }
                                        }
                                        cameraState.animate(
                                            update = CameraUpdateFactory.newLatLngBounds(bounds.build(), 100)
                                        )

                                        bestRoute.transferStop?.let {
                                            Toast.makeText(context, "Changement à $it", Toast.LENGTH_SHORT).show()
                                        }
                                    } else {
                                        Toast.makeText(context, "Aucune correspondance trouvée", Toast.LENGTH_SHORT).show()
                                    }
                                }
                            }
                        )


                    }
                }
            }


//                // 🔄 Bouton de rafraîchissement de la carte
//                GradientIconButton(
//                    icon = Icons.Default.Refresh,
//                    modifier = Modifier
//                        .align(Alignment.BottomStart)
//                        .padding(40.dp)
//                ) {
//                    refreshMap(context)
//                }

        }
    }
    // -----------------------------------
    // Arrêter les updates de localisation à la sortie
    // -----------------------------------
    DisposableEffect(Unit) {
        onDispose { fusedClient.removeLocationUpdates(locationCallback) }
    }
}


fun refreshMap(context: Context) {
    try {
        MapsInitializer.initialize(context, MapsInitializer.Renderer.LATEST) { renderer ->
            val mode: String
            when (renderer) {
                MapsInitializer.Renderer.LATEST -> mode = "LATEST"
                MapsInitializer.Renderer.LEGACY -> mode = "LEGACY"
                else -> mode = "UNKNOWN"
            }
            Log.d("MAPS", "🔄 Carte rafraîchie (renderer = $mode)")
            Toast.makeText(context, "Carte rechargée", Toast.LENGTH_SHORT).show()
        }
    } catch (e: Exception) {
        Log.e("MAPS", "Erreur lors du rafraîchissement de la carte", e)
        Toast.makeText(context, "Erreur de rafraîchissement", Toast.LENGTH_SHORT).show()
    }
}
fun centerOnUser(
    context: Context,
    fusedClient: FusedLocationProviderClient,
    cameraState: CameraPositionState
) {
    // Vérifie la permission
    if (ActivityCompat.checkSelfPermission(
            context,
            Manifest.permission.ACCESS_FINE_LOCATION
        ) != PackageManager.PERMISSION_GRANTED
    ) {
        Log.e("GEOLOC", "Permission de localisation refusée")
        return
    }

    // Récupère la dernière position connue
    fusedClient.lastLocation
        .addOnSuccessListener { location ->

            if (location != null ) {
                // Met à jour la caméra
                val latLng = LatLng(location.latitude, location.longitude)
                // ⚠️ on doit lancer dans une coroutine du Main thread
                CoroutineScope(Dispatchers.Main).launch {
                    cameraState.animate(
                        update = CameraUpdateFactory.newLatLngZoom(latLng, 15f),
                        durationMs = 800
                    )
                }
            } else {
                Log.e("GEOLOC", "Aucune localisation disponible")
            }
        }
        .addOnFailureListener { e ->
            Log.e("GEOLOC", "Erreur lors de la récupération de la localisation: ${e.message}")
        }
}

/**
 * 🔒 Sécurise le démarrage des updates de localisation
 */
private fun startLocationUpdatesSafely(
    context: Context,
    fusedClient: FusedLocationProviderClient,
    locationRequest: LocationRequest,
    callback: LocationCallback
) {
    if (ContextCompat.checkSelfPermission(
            context, Manifest.permission.ACCESS_FINE_LOCATION
        ) == PackageManager.PERMISSION_GRANTED
    ) {
        try {
            fusedClient.requestLocationUpdates(locationRequest, callback, Looper.getMainLooper())
        } catch (e: Exception) {
            Log.e("BusMapScreen", "Erreur lors du démarrage de la localisation : ${e.message}")
        }
    }
}

fun getSpeechIntent(): Intent {
    return Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH).apply {
        putExtra(
            RecognizerIntent.EXTRA_LANGUAGE_MODEL,
            RecognizerIntent.LANGUAGE_MODEL_FREE_FORM
        )
        putExtra(
            RecognizerIntent.EXTRA_LANGUAGE,
            Locale.getDefault()
        )
        putExtra(
            RecognizerIntent.EXTRA_PROMPT,
            "Dites le nom d’un arrêt de bus"
        )
    }
}


@Composable
fun TopActionButtons(
    showFavorites: Boolean,
    onToggleFavorites: () -> Unit,
    onSpeechClick: () -> Unit,
    onLocateClick: () -> Unit, // ✅ Ajout du bouton géoloc
    modifier: Modifier = Modifier,
    onShowRoutePanelChange: (Boolean) -> Unit, // ✅ nouveau callback
    showRoutePanel: Boolean,

) {
    Card(
        modifier = modifier
            .padding(16.dp)
            .background(Color.Transparent),
        colors = CardDefaults.cardColors(containerColor = Color.White),
        shape = RoundedCornerShape(28.dp),
        elevation = CardDefaults.cardElevation(defaultElevation = 6.dp)
    ) {
        Row(
            modifier = Modifier.padding(horizontal = 12.dp, vertical = 8.dp),
            verticalAlignment = Alignment.CenterVertically,
            horizontalArrangement = Arrangement.spacedBy(8.dp)
        ) {
            // ⭐ Bouton favoris
            GradientIconButton(
                icon = if (showFavorites) Icons.Default.KeyboardArrowDown else Icons.Default.Star,
                onClick = onToggleFavorites
            )

            // 🎤 Bouton micro
            GradientIconButton(
                icon = Icons.Default.Mic,
                onClick = onSpeechClick
            )

            // 📍 Bouton géolocalisation
            GradientIconButton(
                icon = Icons.Default.MyLocation,
                onClick = onLocateClick
            )
            // 📍 Bouton Intinéraire
            GradientIconButton(
                icon = Icons.Default.Directions,
                onClick =  {onShowRoutePanelChange(true)}
                        //{navController.navigate("route_search")}
            )
        }
    }
}
@Composable
fun LoadingDialog() {
    AlertDialog(
        onDismissRequest = { /* impossible de fermer */ },
        confirmButton = {},
        title = { Text("Chargement...") },
        text = {
            Row(Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.Center) {
                CircularProgressIndicator()
                Spacer(Modifier.width(16.dp))
                Text("Récupération des horaires du bus...")
            }
        }
    )
}


suspend fun loadStopsAndFavorites(
    context: Context,
    stops: MutableState<List<StopFields>>,
    addressQuery: String,
    addressSuggestions: SnapshotStateList<String>,
    favoriteStops: SnapshotStateList<String>,
    markers: SnapshotStateList<LatLng>,
    geocodeCache: MutableMap<String, LatLng>,
    cameraState: CameraPositionState
    //coroutineScope: CoroutineScope
) {

        try {
            // 1️⃣ Charger les arrêts et suggestions en parallèle (I/O)
            val (fetchedStops, allSuggestions) = coroutineScope {
                val stopsDeferred = async(Dispatchers.IO) { fetchStops() }
                val suggestionsDeferred = async(Dispatchers.IO) {
                    fetchStopPredictions(
                        addressQuery
                    )
                }
                stopsDeferred.await() to suggestionsDeferred.await()
            }

            // 2️⃣ Mettre à jour les états sur le thread principal
            withContext(Dispatchers.Main) {
                stops.value = fetchedStops
                addressSuggestions.clear()
                addressSuggestions.addAll(allSuggestions)

                    cameraState.animate(
                        CameraUpdateFactory.newLatLngZoom(
                            LatLng(46.1603, -1.1511),
                            15f
                        )
                    )
            }

            // 3️⃣ Charger les favoris (en dehors du Main)
            getFavorites(context).collectLatest { saved ->
                withContext(Dispatchers.Main) {
                    favoriteStops.clear()
                    favoriteStops.addAll(saved)
                    refreshMarkers(context, favoriteStops, markers, geocodeCache)
                }
            }

        } catch (e: Exception) {
            Log.e("LOAD_STOPS", "Erreur lors du chargement des arrêts ou favoris: ${e.message}", e)
        }

}
@Composable
fun SearchBarSection(
    context: Context,
    fusedClient: FusedLocationProviderClient,
    addressQuery: String,
    onQueryChange: (String) -> Unit,
    stops: List<StopFields>,
    addressSuggestions: List<String>,
    geocodeCache: MutableMap<String, LatLng>,
    markers: SnapshotStateList<LatLng>,
    favoriteStops: SnapshotStateList<String>,
    cameraState: CameraPositionState,
    coroutineScope: CoroutineScope,
    speechLauncher: ActivityResultLauncher<Intent>
) {
    val focusRequester = remember { FocusRequester() }

    AddressOrStopSearchBar(
        addressQuery = addressQuery,
        onQueryChange = onQueryChange,
        stops = stops,
        onSuggestionClick = { selected ->
            coroutineScope.launch {
                val stop = stops.find { it.stop_name == selected }
                val latLng = runCatching {
                    stop?.let { LatLng(it.stop_lat.toDoubleOrNull() ?: return@launch, it.stop_lon.toDoubleOrNull() ?: return@launch) }
                        ?: geocodeCache[selected]
                        ?: geocodeAddressSuspend(context, selected)?.also { geocodeCache[selected] = it }
                }.getOrNull()

                latLng?.let {
                    try {
                        cameraState.animate(CameraUpdateFactory.newLatLngZoom(it, 16f))
                    } catch (_: Exception) { /* ignore animation errors */ }
                }
            }
        },
        onAddFavorite = { fav ->
            if (fav.isNotBlank() && !favoriteStops.contains(fav)) {
                favoriteStops.add(fav)
                coroutineScope.launch {
                    runCatching {
                        saveFavorites(context, favoriteStops)
                        refreshMarkers(context, favoriteStops, markers, geocodeCache)
                    }
                }
            }
        },
        onSearchClick = {
            coroutineScope.launch {
                if (addressQuery.isNotBlank()) {
                    val latLng = runCatching { geocodeAddressSuspend(context, addressQuery) }.getOrNull()
                    latLng?.let {
                        try {
                            cameraState.animate(CameraUpdateFactory.newLatLngZoom(it, 16f))
                        } catch (_: Exception) { /* ignore animation errors */ }
                    }
                }
            }
        },
        modifier = Modifier.focusRequester(focusRequester)
    )

    // Force le focus et ouvre le clavier au lancement
    LaunchedEffect(Unit) {
        runCatching { focusRequester.requestFocus() }
    }
}


@SuppressLint("MissingPermission")
fun startLocationUpdates(
    fusedClient: FusedLocationProviderClient,
    locationRequest: LocationRequest,
    callback: LocationCallback
) {
    try {
        fusedClient.requestLocationUpdates(
            locationRequest,
            callback,
            Looper.getMainLooper()
        )
    } catch (e: SecurityException) {
        Log.e("MAP", "Permission manquante : ${e.message}")
    }
}

// refreshMarkers : reconstruit markers à partir de la liste favoriteStops
suspend fun refreshMarkers(
    context: Context,
    favoriteStops: List<String>,
    markers: SnapshotStateList<LatLng>,
    cache: MutableMap<String, LatLng>
) {
    markers.clear()
    for (fav in favoriteStops) {
        val latLng = cache[fav] ?: geocodeAddressSuspend(
            context,
            fav
        )?.also { cache[fav] = it }
        latLng?.let { markers.add(it) }
    }
}

@SuppressLint("UnrememberedMutableState")
@OptIn(MapsComposeExperimentalApi::class)
@Composable
fun MapSection(
    hasPermission: Boolean,
    cameraState: CameraPositionState,
    routePolylines: MutableState<Map<String, RoutePolylineData>>,
    stops: List<StopFields>,
    markers: SnapshotStateList<LatLng>,
    isLoading: MutableState<Boolean>,
    selectedStop: MutableState<StopFields?>,
    context: Context,
    coroutineScope: CoroutineScope,
    onMapLoaded: () -> Unit,
    modifier: Modifier = Modifier,
    showRoutePanel: Boolean,
    routeStart: LatLng?,
    routeEnd: LatLng?,
    routePoints: MutableState<Map<String, RoutePolylineData>>
) {
    if (!hasPermission) return
    val snippetsByStop = remember { mutableStateMapOf<String, String>() }
    var lastSelectedLineName by remember { mutableStateOf("") }
    var lastSelectedLineColor by remember { mutableStateOf("#1971DE") }

    GoogleMap(
        modifier = Modifier.fillMaxSize(),
        properties = MapProperties(isMyLocationEnabled = true),
        uiSettings = MapUiSettings(
            zoomControlsEnabled = true,
            compassEnabled = true,
            myLocationButtonEnabled = false
        ),
        contentPadding = PaddingValues(
            bottom = 48.dp,
            start = 8.dp,
            end = 8.dp,
            top = 8.dp
        ),
        cameraPositionState = cameraState,
        onMapLoaded = onMapLoaded
    ) {
        // --- Affichage des arrêts
        stops.forEach { stop ->
            if (stop.location_type == "0") {
                MarkerInfoWindow(
                    state = rememberMarkerState( position = LatLng(stop.stop_lat.toDouble(),stop.stop_lon.toDouble() )
                    ),
                    title = stop.stop_name,
                    snippet = snippetsByStop[stop.stop_id] ?: "Chargement...",
                    icon = bitmapDescriptorFromVectorWithBackground(
                        context,
                        R.drawable.baseline_directions_bus_24,
                        android.graphics.Color.YELLOW,
                        1.2f
                    ),
                    onClick = { marker ->
                        coroutineScope.launch {
                            // 🟢 Indique que cet arrêt est sélectionné
                            selectedStop.value = stop
                            safeHandleStopClick(
                                stop,
                                selectedStop,
                                setLoading = { isLoading.value = it }, // <-- correct
                                updateSnippet = { snippet ->
                                    snippetsByStop[stop.stop_id] = snippet  // ✅ met à jour le texte spécifique à cet arrêt
                                },
                                routePolylines = routePolylines,
                                stops = stops,
                                updateLineInfo = { name, color ->
                                    lastSelectedLineName = name
                                    lastSelectedLineColor = color
                                }                            )
                            marker.showInfoWindow()
                        }

                        true
                    }
                ) { marker ->
                    Column(
                        modifier = Modifier
                            .background(Color(0xFF1971DE), RoundedCornerShape(8.dp)) // 💙 fond bleu
                            .padding(8.dp)
                    ) {
                        Text(marker.title ?: "",color = Color.White,  style = MaterialTheme.typography.titleSmall)
                        Spacer(Modifier.height(4.dp))
                        Text(snippetsByStop[stop.stop_id] ?: "Chargement...", color = Color.White, style = MaterialTheme.typography.bodySmall)
                    }
                }

            }
        }

        // --- Polylines avec couleur propre à chaque ligne
        routePolylines.value.forEach { (_, routeData) ->
            if (routeData.points.size >= 2) {
                Polyline(
                    points = routeData.points,
                    color = Color(routeData.color.toColorInt()),
                    width = 16f
                )
            }
        }
        // --- Favorite markers
        if (markers.isNotEmpty()) {
            markers.toList().forEach { pos ->
                Marker(
                    state = rememberMarkerState(position = pos),
                    title = "Favori",
                    icon = BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_AZURE)
                )
            }
        }

//        if (routeStart != null) {
//            Marker(
//                state = MarkerState(position = routeStart!!),
//                title = "Départ",
//                icon = BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_GREEN)
//            )
//        }
//
//        if (routeEnd != null) {
//            Marker(
//                state = MarkerState(position = routeEnd!!),
//                title = "Arrivée",
//                icon = BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_RED)
//            )
//        }
//
//        routePoints.value.forEach { (_, routeData) ->
//            if (routeData.points.size >= 2) {
//                Polyline(
//                    points = routeData.points,
//                    color = Color(routeData.color.toColorInt()),
//                    width = 16f
//                )
//            }
//        }

    }
    if (lastSelectedLineName.isNotEmpty()) Box(
        modifier = modifier
        ) {
            Text(text = lastSelectedLineName, color = Color(android.graphics.Color.parseColor(lastSelectedLineColor)), style = MaterialTheme.typography.titleMedium, textAlign = TextAlign.Center, modifier = Modifier.fillMaxWidth())
        }
    }



suspend fun safeHandleStopClick(
    stop: StopFields,
    selectedStop: MutableState<StopFields?>,
    setLoading: (Boolean) -> Unit,
    updateSnippet: (String) -> Unit,
    routePolylines: MutableState<Map<String, RoutePolylineData>>,
    stops: List<StopFields>,
    updateLineInfo: (String, String) -> Unit
) {
    setLoading(true)
    selectedStop.value = stop
    try {
        val stopTimes = withContext(Dispatchers.IO) {
            fetchStopTimes(stop.stop_code)
        }
        val snippet = if (stopTimes.isNotEmpty()) {
            getNextTwoTimesWithLineName(stopTimes).joinToString("\n")
        } else {
            "Aucun bus à venir"
        }
        // 3️⃣ Récupère l’ID du premier trip et le nom de la ligne
        val tripId = stopTimes.firstOrNull()?.trip_id
        if (tripId != null) {
            // 4️⃣ Récupère les polylines + infos de ligne (en IO)
            val newPolylines = withContext(Dispatchers.IO) {
                fetchRoutePolylinesByTrip_id(stops, tripId)
            }
            routePolylines.value = mutableMapOf<String, RoutePolylineData>().apply {
                putAll(newPolylines)
            }
            // ✅ Récupère le nom + couleur de la ligne et les renvoie
            val lineInfo = newPolylines.values.firstOrNull()
            lineInfo?.let {
                val lineName = lineInfo?.lineName ?: "Inconnue"
                val lineColor = lineInfo?.color ?: "#1971DE"
                updateLineInfo(lineName, lineColor)
            }
        }
        updateSnippet(snippet)
    } catch (e: Exception) {
        e.printStackTrace()
        updateSnippet("Erreur de chargement")
        updateLineInfo("Erreur", "#1971DE")
    } finally {
        setLoading(false)
    }
}

@Composable
fun FavoritesPanel(
    showFavorites: Boolean,
    favoriteStops: SnapshotStateList<String>,
    markers: SnapshotStateList<LatLng>,
    geocodeCache: MutableMap<String, LatLng>,
    cameraState: CameraPositionState,
    context: Context,
    coroutineScope: CoroutineScope,
    modifier: Modifier = Modifier
) {
    AnimatedVisibility(
        visible = showFavorites,
        enter = slideInVertically { it } + fadeIn(),
        exit = slideOutVertically { it } + fadeOut(),
        modifier = modifier
    ) {
        Card(
            modifier = modifier
                .fillMaxWidth()
                .heightIn(min = 100.dp, max = 250.dp)
                .padding(8.dp),
            colors = CardDefaults.cardColors(containerColor = Color.White),
            shape = RoundedCornerShape(12.dp),
            elevation = CardDefaults.cardElevation(6.dp)
        ) {
            Column(
                Modifier
                    .fillMaxSize()
                    .background(
                        Brush.verticalGradient(
                            listOf(Color(0xFF1971DE), Color(0xFF1560BD))
                        )
                    )
            ) {
                // En-tête
                Box(
                    modifier = Modifier
                        .fillMaxWidth()
                        .background(Color.White),
                    contentAlignment = Alignment.Center
                ) {
                    Row(verticalAlignment = Alignment.CenterVertically) {
                        Spacer(Modifier.width(6.dp))
                        Icon(
                            imageVector = Icons.Filled.Star,
                            contentDescription = null,
                            tint = Color(0xFF1971DE)
                        )
                        Spacer(Modifier.width(6.dp))
                        Text(
                            "Arrêts favoris",
                            style = MaterialTheme.typography.titleMedium.copy(color = Color(0xFF1971DE)),
                            modifier = Modifier.padding(8.dp),
                            textAlign = TextAlign.Center
                        )
                    }
                }

                LazyColumn(
                    modifier = Modifier
                        .fillMaxSize()
                        .padding(horizontal = 8.dp, vertical = 4.dp)
                ) {
                    items(favoriteStops) { stop ->
                        Row(
                            modifier = Modifier
                                .fillMaxWidth()
                                .padding(vertical = 2.dp)
                                .clip(RoundedCornerShape(6.dp))
                                .background(Color(0xFF1971DE))
                                .clickable {
                                    coroutineScope.launch {
                                        runCatching {
                                            val latLng = geocodeCache[stop]
                                                ?: geocodeAddressSuspend(context, stop)?.also {
                                                    geocodeCache[stop] = it
                                                }
                                            latLng?.let {
                                                cameraState.animate(
                                                    CameraUpdateFactory.newLatLngZoom(it, 16f)
                                                )
                                                if (!markers.any { m -> areSameLatLng(m, it) }) {
                                                    markers.add(it)
                                                }
                                            }
                                        }
                                    }
                                }
                                .padding(vertical = 6.dp, horizontal = 10.dp),
                            verticalAlignment = Alignment.CenterVertically
                        ) {
                            Text(
                                text = stop,
                                color = Color.White,
                                style = MaterialTheme.typography.bodyMedium,
                                modifier = Modifier.weight(1f)
                            )
                            IconButton(onClick = {
                                runCatching {
                                    favoriteStops.remove(stop)
                                    coroutineScope.launch {
                                        saveFavorites(context, favoriteStops)
                                        refreshMarkers(context, favoriteStops, markers, geocodeCache)
                                    }
                                }
                            }) {
                                Icon(
                                    imageVector = Icons.Default.Delete,
                                    contentDescription = "Supprimer",
                                    tint = Color.White
                                )
                            }
                        }
                    }
                }
            }
        }
    }
}


fun bitmapDescriptorFromVectorWithBackground(
    context: Context,
    vectorResId: Int,
    backgroundColor: Int = android.graphics.Color.YELLOW,
    zoom: Float = 1.0F,
): BitmapDescriptor {
    val size = (zoom * 2).toInt().coerceIn(20, 100)
    val vectorDrawable = ContextCompat.getDrawable(context, vectorResId)!!
    val sizePx = (size * context.resources.displayMetrics.density).toInt()

    val bitmap = createBitmap(sizePx, sizePx)
    val canvas = Canvas(bitmap)

    // Dessiner le cercle de fond
    // Dessine le cercle de fond avec android.graphics.Paint
    val paint = Paint().apply {
        color = backgroundColor
        isAntiAlias = true
    }
    val radius = (sizePx / 2f)
    canvas.drawCircle(radius, radius, radius, paint)

    // Icône plus petite et centrée
    val iconSize = ((sizePx * 0.6f)).toInt()
    val left = (sizePx - iconSize) / 2
    val top = (sizePx - iconSize) / 2
    val right = left + iconSize
    val bottom = top + iconSize

    vectorDrawable.setBounds(left, top, right, bottom)
    vectorDrawable.setTint(android.graphics.Color.BLACK) // icône en blanc
    vectorDrawable.draw(canvas)

    return BitmapDescriptorFactory.fromBitmap(bitmap)
}


@Composable
fun GradientIconButton(
    icon: ImageVector,
    modifier: Modifier = Modifier,
    onClick: () -> Unit
) {
    Button(
        onClick = onClick,
        colors = ButtonDefaults.buttonColors(Color.Transparent),
        contentPadding = PaddingValues(0.dp),
        shape = RoundedCornerShape(24.dp), // arrondi
        modifier = modifier //Modifier.size(50.dp)    // carré 50x50
    ) {
        Box(
            modifier = modifier,
                //.fillMaxSize(),
                //.background(myGradientBlue, RoundedCornerShape(12.dp)),
            contentAlignment = Alignment.Center
        ) {
            Icon(
                imageVector = icon,
                contentDescription = null,
                tint = Color(0xFF1565C0) // bleu Google Maps
            )
        }
    }
   
}
    // utilitaire comparaison pour éviter doublons
    fun areSameLatLng(a: LatLng, b: LatLng, eps: Double = 1e-6) =
        abs(a.latitude - b.latitude) < eps && abs(a.longitude - b.longitude) < eps


