/**
 * rota.js — Roteamento real por ruas usando OSRM
 * Respeita esquinas, quarteirões e sentido das vias.
 *
 * Função principal: calcularERenderRota(map, lat1, lng1, lat2, lng2, layerRef, opts)
 * Retorna: { distancia_m, distancia_km, duracao, fonte, latlngs }
 *
 * Fontes em ordem de tentativa:
 *   1. OSRM público (router.project-osrm.org) — rota real por ruas
 *   2. OSRM demo alternativo (routing.openstreetmap.de)
 *   3. Haversine (linha reta) — fallback final
 */

// ── Configuração ─────────────────────────────────────────────────────
const OSRM_SERVERS = [
    'https://router.project-osrm.org',
    'https://routing.openstreetmap.de/routed-car',
];
const OSRM_TIMEOUT_MS = 8000;

// ── Haversine (fallback) ──────────────────────────────────────────────
function _haversineM(lat1, lon1, lat2, lon2) {
    const R = 6371e3, r = Math.PI / 180;
    const dLat = (lat2 - lat1) * r;
    const dLon = (lon2 - lon1) * r;
    const a = Math.sin(dLat / 2) ** 2 +
        Math.cos(lat1 * r) * Math.cos(lat2 * r) * Math.sin(dLon / 2) ** 2;
    return R * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
}

// ── Decodifica polyline encoded (formato Google/OSRM) ─────────────────
function _decodePolyline(encoded) {
    const pts = [];
    let idx = 0, lat = 0, lng = 0;
    while (idx < encoded.length) {
        let b, shift = 0, result = 0;
        do { b = encoded.charCodeAt(idx++) - 63; result |= (b & 0x1f) << shift; shift += 5; } while (b >= 0x20);
        lat += (result & 1) ? ~(result >> 1) : result >> 1;
        shift = 0; result = 0;
        do { b = encoded.charCodeAt(idx++) - 63; result |= (b & 0x1f) << shift; shift += 5; } while (b >= 0x20);
        lng += (result & 1) ? ~(result >> 1) : result >> 1;
        pts.push([lat / 1e5, lng / 1e5]);
    }
    return pts;
}

// ── Consulta OSRM ─────────────────────────────────────────────────────
async function _consultarOSRM(server, lat1, lon1, lat2, lon2) {
    const url = `${server}/route/v1/driving/${lon1},${lat1};${lon2},${lat2}` +
        `?overview=full&geometries=polyline&steps=true`;

    const ctrl = new AbortController();
    const timer = setTimeout(() => ctrl.abort(), OSRM_TIMEOUT_MS);

    try {
        const resp = await fetch(url, { signal: ctrl.signal });
        clearTimeout(timer);
        if (!resp.ok) throw new Error(`HTTP ${resp.status}`);
        const data = await resp.json();
        if (data.code !== 'Ok' || !data.routes?.length) throw new Error('Sem rota');

        const route = data.routes[0];
        const latlngs = _decodePolyline(route.geometry);

        // Extrai passos de navegação
        const steps = [];
        route.legs?.[0]?.steps?.forEach(s => {
            if (s.maneuver?.type && s.distance > 5) {
                steps.push({
                    tipo: s.maneuver.type,
                    modificador: s.maneuver.modifier || '',
                    distancia: Math.round(s.distance),
                    nome: s.name || '',
                });
            }
        });

        return {
            ok: true,
            distancia_m: Math.round(route.distance),
            distancia_km: (route.distance / 1000).toFixed(2),
            duracao_s: Math.round(route.duration),
            duracao: _fmtDuracao(route.duration),
            latlngs,
            steps,
            fonte: 'osrm',
        };
    } catch (e) {
        clearTimeout(timer);
        throw e;
    }
}

// ── Formata duração ───────────────────────────────────────────────────
function _fmtDuracao(segundos) {
    const m = Math.round(segundos / 60);
    if (m < 60) return `${m} min`;
    const h = Math.floor(m / 60);
    const r = m % 60;
    return r > 0 ? `${h}h ${r}min` : `${h}h`;
}

// ── Ícone por manobra ─────────────────────────────────────────────────
function _iconeManobra(tipo, mod) {
    if (tipo === 'arrive') return '🏁';
    if (tipo === 'depart') return '🚀';
    if (mod === 'left' || mod === 'sharp left') return '⬅️';
    if (mod === 'right' || mod === 'sharp right') return '➡️';
    if (mod === 'slight left') return '↖️';
    if (mod === 'slight right') return '↗️';
    if (mod === 'uturn') return '🔄';
    return '⬆️';
}

// ── Estilo da linha por fonte ─────────────────────────────────────────
function _estiloLinha(fonte, cor) {
    if (fonte === 'osrm') {
        return { color: cor || '#00d4ff', weight: 5, opacity: 0.85, dashArray: null };
    }
    // Fallback haversine = linha pontilhada âmbar
    return { color: '#f59e0b', weight: 4, opacity: 0.7, dashArray: '10 6' };
}

// ─────────────────────────────────────────────────────────────────────
// FUNÇÃO PRINCIPAL
// ─────────────────────────────────────────────────────────────────────
/**
 * calcularERenderRota
 *
 * @param {L.Map}   map       — instância Leaflet
 * @param {number}  lat1      — latitude origem
 * @param {number}  lng1      — longitude origem
 * @param {number}  lat2      — latitude destino
 * @param {number}  lng2      — longitude destino
 * @param {object}  layerRef  — { layers: [] } para controle de camadas
 * @param {object}  opts      — { cor, onKpis, mostrarSteps, containerId }
 *
 * opts.cor          — cor da linha (padrão: '#00d4ff')
 * opts.onKpis       — callback(resultado) chamado com os KPIs
 * opts.mostrarSteps — se true, renderiza passos no opts.containerId
 * opts.containerId  — ID do elemento onde renderizar os passos
 *
 * @returns {Promise<object>} resultado com distancia_m, distancia_km, duracao, fonte, latlngs
 */
async function calcularERenderRota(map, lat1, lng1, lat2, lng2, layerRef, opts = {}) {
    const cor = opts.cor || '#00d4ff';

    // Remove camadas anteriores
    if (layerRef?.layers?.length) {
        layerRef.layers.forEach(l => { try { map.removeLayer(l); } catch (e) { } });
        layerRef.layers = [];
    }

    let resultado = null;

    // 1. Tenta OSRM em cada servidor
    for (const server of OSRM_SERVERS) {
        try {
            resultado = await _consultarOSRM(server, lat1, lng1, lat2, lng2);
            break; // sucesso — para de tentar
        } catch (e) {
            console.warn(`OSRM ${server} falhou:`, e.message);
        }
    }

    // 2. Fallback haversine
    if (!resultado) {
        const distM = _haversineM(lat1, lng1, lat2, lng2);
        resultado = {
            ok: false,
            distancia_m: Math.round(distM),
            distancia_km: (distM / 1000).toFixed(2),
            duracao_s: Math.round(distM / 8), // ~29km/h médio urbano
            duracao: _fmtDuracao(distM / 8),
            latlngs: [[lat1, lng1], [lat2, lng2]],
            steps: [],
            fonte: 'haversine',
        };
    }

    // 3. Desenha a linha no mapa
    const estilo = _estiloLinha(resultado.fonte, cor);
    const linha = L.polyline(resultado.latlngs, estilo).addTo(map);

    // Sombra para OSRM (linha dupla)
    let sombra = null;
    if (resultado.fonte === 'osrm') {
        sombra = L.polyline(resultado.latlngs, {
            color: '#000', weight: 8, opacity: 0.18, dashArray: null
        }).addTo(map);
        sombra.bringToBack();
    }

    // Marcadores de origem/destino
    const iconOri = L.divIcon({
        className: '',
        html: `<div style="width:14px;height:14px;border-radius:50%;background:${cor};border:2px solid #fff;box-shadow:0 0 8px ${cor}"></div>`,
        iconSize: [14, 14], iconAnchor: [7, 7]
    });
    const iconDst = L.divIcon({
        className: '',
        html: `<div style="width:14px;height:14px;border-radius:50%;background:#ef4444;border:2px solid #fff;box-shadow:0 0 8px #ef4444"></div>`,
        iconSize: [14, 14], iconAnchor: [7, 7]
    });
    const mOri = L.marker([lat1, lng1], { icon: iconOri }).addTo(map);
    const mDst = L.marker([lat2, lng2], { icon: iconDst }).addTo(map);

    // Registra camadas para cleanup futuro
    layerRef.layers = [linha, mOri, mDst];
    if (sombra) layerRef.layers.push(sombra);

    // Ajusta o mapa para mostrar toda a rota
    try {
        map.fitBounds(L.latLngBounds(resultado.latlngs), { padding: [32, 32], maxZoom: 17 });
    } catch (e) { }

    // 4. Callback de KPIs
    if (typeof opts.onKpis === 'function') {
        opts.onKpis(resultado);
    }

    // 5. Renderiza passos de navegação (opcional)
    if (opts.mostrarSteps && opts.containerId && resultado.steps.length > 0) {
        const cont = document.getElementById(opts.containerId);
        if (cont) {
            cont.innerHTML = resultado.steps.slice(0, 10).map(s => `
                <div style="display:flex;gap:10px;padding:6px 0;border-bottom:1px solid var(--border);font-size:11px">
                    <div style="width:22px;text-align:center">${_iconeManobra(s.tipo, s.modificador)}</div>
                    <div style="flex:1;color:var(--text)">${s.nome || _labelManobra(s.tipo, s.modificador)}</div>
                    <div style="color:var(--cyan);font-family:var(--mono)">${s.distancia >= 1000 ? (s.distancia/1000).toFixed(1)+'km' : s.distancia+'m'}</div>
                </div>
            `).join('');
        }
    }

    return resultado;
}

// ── Label textual da manobra ──────────────────────────────────────────
function _labelManobra(tipo, mod) {
    const map = {
        'depart': 'Siga em frente',
        'arrive': 'Destino alcançado',
        'turn left': 'Vire à esquerda',
        'turn right': 'Vire à direita',
        'turn sharp left': 'Vire à esquerda (curva fechada)',
        'turn sharp right': 'Vire à direita (curva fechada)',
        'turn slight left': 'Vire levemente à esquerda',
        'turn slight right': 'Vire levemente à direita',
        'continue': 'Continue em frente',
        'roundabout': 'Entre na rotatória',
        'exit roundabout': 'Saia da rotatória',
        'uturn': 'Retorne',
    };
    const key = mod ? `${tipo} ${mod}` : tipo;
    return map[key] || map[tipo] || 'Continue';
}
