"use client";

import { MapContainer, TileLayer, GeoJSON, useMap } from "react-leaflet";
import "leaflet/dist/leaflet.css";
import { useEffect, useState, useRef } from "react";
import { Layers, X, Eye, EyeOff, Trash2, ChevronDown, ChevronUp, MoreHorizontal, Settings, GripVertical } from "lucide-react";
import L from "leaflet";
import {
    DndContext,
    closestCenter,
    KeyboardSensor,
    PointerSensor,
    useSensor,
    useSensors,
    DragEndEvent
} from '@dnd-kit/core';
import {
    arrayMove,
    SortableContext,
    sortableKeyboardCoordinates,
    verticalListSortingStrategy,
    useSortable
} from '@dnd-kit/sortable';
import { CSS } from '@dnd-kit/utilities';

// Fix for default marker icons in Next.js
delete (L.Icon.Default.prototype as any)._getIconUrl;
L.Icon.Default.mergeOptions({
    iconRetinaUrl: "https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.7.1/images/marker-icon-2x.png",
    iconUrl: "https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.7.1/images/marker-icon.png",
    shadowUrl: "https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.7.1/images/marker-shadow.png",
});

export interface MapLayer {
    id: string;
    name: string;
    data: any;
    visible: boolean;
    style: {
        color: string;
        fillColor: string;
        fillOpacity: number;
        weight: number;
        opacity?: number;
    };
    // Choropleth configuration
    choropleth?: {
        enabled: boolean;
        column: string;
        palette: string;
        scale?: 'linear' | 'log';
        min?: number;
        max?: number;
    };
    // Geometry type for styling
    geometryType?: 'polygon' | 'point' | 'line';
    // Point marker configuration
    pointMarker?: {
        icon: string;  // emoji or icon class
        color: string;
        size: number;
        style?: string; // "circle" or undefined
    };
}

interface MapViewerProps {
    layers: MapLayer[];
    onLayerUpdate: (id: string, updates: Partial<MapLayer>) => void;
    onLayerRemove: (id: string) => void;
    onLayerReorder: (fromIndex: number, toIndex: number) => void;
}

// ============== Choropleth Color Scales =================
const COLOR_PALETTES: Record<string, string[]> = {
    viridis: ['#440154', '#482878', '#3e4a89', '#31688e', '#26828e', '#1f9e89', '#35b779', '#6ece58', '#b5de2b', '#fde725'],
    blues: ['#f7fbff', '#deebf7', '#c6dbef', '#9ecae1', '#6baed6', '#4292c6', '#2171b5', '#08519c', '#08306b'],
    reds: ['#fff5f0', '#fee0d2', '#fcbba1', '#fc9272', '#fb6a4a', '#ef3b2c', '#cb181d', '#a50f15', '#67000d'],
    greens: ['#f7fcf5', '#e5f5e0', '#c7e9c0', '#a1d99b', '#74c476', '#41ab5d', '#238b45', '#006d2c', '#00441b'],
    oranges: ['#fff5eb', '#fee6ce', '#fdd0a2', '#fdae6b', '#fd8d3c', '#f16913', '#d94801', '#a63603', '#7f2704'],
};

function getChoroplethColor(value: number, min: number, max: number, palette: string, scale: 'linear' | 'log' = 'linear'): string {
    const colors = COLOR_PALETTES[palette] || COLOR_PALETTES.viridis;
    if (value <= min) return colors[0];
    if (value >= max) return colors[colors.length - 1];

    let normalized: number;

    if (scale === 'log' && min > 0 && max > 0) {
        // Logarithmic normalization
        const logMin = Math.log(min);
        const logMax = Math.log(max);
        const logVal = Math.log(Math.max(min, value)); // avoid log(0)
        normalized = (logVal - logMin) / (logMax - logMin);
    } else {
        // Linear normalization
        normalized = (value - min) / (max - min);
    }

    // Clamp
    normalized = Math.max(0, Math.min(1, normalized));

    // Map to color index
    const index = Math.floor(normalized * (colors.length - 1));
    return colors[index];
}

function calculateMinMax(features: any[], column: string): { min: number; max: number } {
    let min = Infinity;
    let max = -Infinity;

    features.forEach((feature: any) => {
        const value = feature.properties?.[column];
        if (typeof value === 'number' && !isNaN(value)) {
            min = Math.min(min, value);
            max = Math.max(max, value);
        }
    });

    return { min: min === Infinity ? 0 : min, max: max === -Infinity ? 1 : max };
}

// ============== Sortable Item Component =================
interface SortableLayerItemProps {
    layer: MapLayer;
    onUpdate: (id: string, updates: Partial<MapLayer>) => void;
    onRemove: (id: string) => void;
    expandedId: string | null;
    setExpandedId: (id: string | null) => void;
}

function SortableLayerItem({ layer, onUpdate, onRemove, expandedId, setExpandedId }: SortableLayerItemProps) {
    const {
        attributes,
        listeners,
        setNodeRef,
        transform,
        transition,
        isDragging
    } = useSortable({ id: layer.id });

    const style = {
        transform: CSS.Transform.toString(transform),
        transition,
        zIndex: isDragging ? 10 : 'auto',
        opacity: isDragging ? 0.5 : 1
    };

    return (
        <div ref={setNodeRef} style={style} className={`bg-slate-50 rounded-lg p-3 border ${isDragging ? 'border-indigo-400 shadow-md' : 'border-slate-100'}`}>
            <div className="flex items-center justify-between mb-2">
                <div className="flex items-center gap-2 overflow-hidden flex-1">
                    {/* Drag Handle */}
                    <div
                        {...attributes}
                        {...listeners}
                        className="cursor-move text-slate-400 hover:text-slate-600 p-0.5 rounded hover:bg-slate-200"
                        title="Drag to reorder"
                    >
                        <GripVertical className="w-4 h-4" />
                    </div>

                    <div
                        className="w-3 h-3 rounded-full shrink-0"
                        style={{ backgroundColor: layer.style.color }}
                    />
                    <span className="text-sm font-medium text-slate-700 overflow-x-auto whitespace-nowrap scrollbar-none" title={layer.name}>
                        {layer.name}
                    </span>
                </div>
                <div className="flex items-center gap-1">
                    <button
                        onClick={() => onUpdate(layer.id, { visible: !layer.visible })}
                        className="p-1 text-slate-400 hover:text-slate-600 hover:bg-slate-200 rounded transition-colors"
                        title={layer.visible ? "Hide layer" : "Show layer"}
                    >
                        {layer.visible ? <Eye className="w-3.5 h-3.5" /> : <EyeOff className="w-3.5 h-3.5" />}
                    </button>
                    <button
                        onClick={() => setExpandedId(expandedId === layer.id ? null : layer.id)}
                        className={`p-1 hover:text-slate-600 hover:bg-slate-200 rounded transition-colors ${expandedId === layer.id ? 'text-indigo-600 bg-indigo-50' : 'text-slate-400'}`}
                        title="Layer settings"
                    >
                        {expandedId === layer.id ? <ChevronUp className="w-3.5 h-3.5" /> : <Settings className="w-3.5 h-3.5" />}
                    </button>
                    <button
                        onClick={() => onRemove(layer.id)}
                        className="p-1 text-slate-400 hover:text-red-500 hover:bg-red-50 rounded transition-colors"
                        title="Remove layer"
                    >
                        <Trash2 className="w-3.5 h-3.5" />
                    </button>
                </div>
            </div>

            {/* Layer Settings (Expanded) */}
            {expandedId === layer.id && (
                <div className="pt-2 mt-2 border-t border-slate-200 space-y-4 animation-slide-down">
                    {/* Fill Settings */}
                    <div className="space-y-2">
                        <label className="text-xs font-semibold text-slate-500 uppercase tracking-wider">Fill Style</label>
                        <div className="flex items-center justify-between">
                            <label className="text-xs text-slate-500">Color</label>
                            <div className="flex items-center gap-2">
                                <input
                                    type="color"
                                    value={layer.style.fillColor}
                                    onChange={(e) => onUpdate(layer.id, {
                                        style: { ...layer.style, fillColor: e.target.value }
                                    })}
                                    className="w-6 h-6 rounded cursor-pointer border-0 p-0"
                                />
                            </div>
                        </div>
                        <div>
                            <div className="flex items-center justify-between mb-1">
                                <label className="text-xs text-slate-500">Opacity</label>
                                <span className="text-xs text-slate-400">{Math.round((layer.style.fillOpacity ?? 0.6) * 100)}%</span>
                            </div>
                            <input
                                type="range"
                                min="0"
                                max="1"
                                step="0.1"
                                value={layer.style.fillOpacity ?? 0.6}
                                onChange={(e) => onUpdate(layer.id, {
                                    style: { ...layer.style, fillOpacity: parseFloat(e.target.value) }
                                })}
                                className="w-full h-1.5 bg-slate-200 rounded-lg appearance-none cursor-pointer accent-indigo-600"
                            />
                        </div>
                    </div>

                    {/* Border Settings */}
                    <div className="space-y-2 pt-2 border-t border-slate-100">
                        <label className="text-xs font-semibold text-slate-500 uppercase tracking-wider">Border Style</label>

                        {/* Border Color */}
                        <div className="flex items-center justify-between">
                            <label className="text-xs text-slate-500">Color</label>
                            <div className="flex items-center gap-2">
                                <input
                                    type="color"
                                    value={layer.style.color}
                                    onChange={(e) => onUpdate(layer.id, {
                                        style: { ...layer.style, color: e.target.value }
                                    })}
                                    className="w-6 h-6 rounded cursor-pointer border-0 p-0"
                                />
                            </div>
                        </div>

                        {/* Border Width */}
                        <div>
                            <div className="flex items-center justify-between mb-1">
                                <label className="text-xs text-slate-500">Width</label>
                                <span className="text-xs text-slate-400">{layer.style.weight ?? 1}px</span>
                            </div>
                            <input
                                type="range"
                                min="0"
                                max="5"
                                step="0.5"
                                value={layer.style.weight ?? 1}
                                onChange={(e) => onUpdate(layer.id, {
                                    style: { ...layer.style, weight: parseFloat(e.target.value) }
                                })}
                                className="w-full h-1.5 bg-slate-200 rounded-lg appearance-none cursor-pointer accent-indigo-600"
                            />
                        </div>

                        {/* Border Opacity (using style.opacity which Leaflet uses for stroke opacity) */}
                        <div>
                            <div className="flex items-center justify-between mb-1">
                                <label className="text-xs text-slate-500">Opacity</label>
                                <span className="text-xs text-slate-400">{Math.round((layer.style.opacity ?? 1) * 100)}%</span>
                            </div>
                            <input
                                type="range"
                                min="0"
                                max="1"
                                step="0.1"
                                value={layer.style.opacity ?? 1}
                                onChange={(e) => onUpdate(layer.id, {
                                    style: { ...layer.style, opacity: parseFloat(e.target.value) }
                                })}
                                className="w-full h-1.5 bg-slate-200 rounded-lg appearance-none cursor-pointer accent-indigo-600"
                            />
                        </div>
                    </div>
                </div>
            )}
        </div>
    );
}

// Component to fit map bounds to the latest added layer
function AutoFitBounds({ layers }: { layers: MapLayer[] }) {
    const map = useMap();
    const prevLayersLength = useRef(0);

    useEffect(() => {
        // Only fit bounds if a NEW layer was added (not on every style update)
        if (layers.length > prevLayersLength.current) {
            const latestLayer = layers[layers.length - 1]; // Latest is at end of array (top layer)
            if (latestLayer && latestLayer.visible && latestLayer.data) {
                try {
                    const geoJsonLayer = L.geoJSON(latestLayer.data);
                    const bounds = geoJsonLayer.getBounds();
                    if (bounds.isValid()) {
                        map.fitBounds(bounds, { padding: [50, 50], maxZoom: 14 });
                    }
                } catch (e) {
                    console.error("Error fitting bounds:", e);
                }
            }
        }
        prevLayersLength.current = layers.length;
    }, [layers, map]);

    return null;
}

// Popup content for features - Rich display with all properties
function onEachFeature(feature: any, layer: L.Layer) {
    if (feature.properties) {
        const props = feature.properties;

        // Start popup with styled container
        let popupContent = `
            <div style="min-width: 200px; font-family: system-ui, sans-serif;">
        `;

        // Determine admin level and show appropriate title
        const title = props.adm3_name || props.adm2_name || props.adm1_name || props.name || "Feature";
        const adminLevel = props.adm3_name ? "Corregimiento" : props.adm2_name ? "District" : props.adm1_name ? "Province" : "";

        popupContent += `
            <div style="border-bottom: 2px solid #6366f1; padding-bottom: 8px; margin-bottom: 8px;">
                <div style="font-size: 14px; font-weight: 600; color: #1e293b;">${title}</div>
                ${adminLevel ? `<div style="font-size: 11px; color: #64748b; text-transform: uppercase; letter-spacing: 0.5px;">${adminLevel}</div>` : ''}
            </div>
        `;

        // Admin hierarchy (if not at country level)
        const hierarchyItems = [];
        if (props.adm3_name && props.adm2_name) hierarchyItems.push(`📍 District: ${props.adm2_name}`);
        if ((props.adm3_name || props.adm2_name) && props.adm1_name) hierarchyItems.push(`🏛️ Province: ${props.adm1_name}`);

        if (hierarchyItems.length > 0) {
            popupContent += `<div style="font-size: 12px; color: #475569; margin-bottom: 8px;">`;
            hierarchyItems.forEach(item => {
                popupContent += `<div>${item}</div>`;
            });
            popupContent += `</div>`;
        }

        // Key metrics section
        const metrics = [];
        if (props.area_sqkm) {
            const area = typeof props.area_sqkm === 'number' ? props.area_sqkm : parseFloat(props.area_sqkm);
            metrics.push({ label: "Area", value: `${area.toLocaleString(undefined, { maximumFractionDigits: 1 })} km²` });
        }
        if (props.area) {
            const area = typeof props.area === 'number' ? props.area : parseFloat(props.area);
            metrics.push({ label: "Area", value: `${area.toLocaleString(undefined, { maximumFractionDigits: 1 })} km²` });
        }
        if (props.population) {
            metrics.push({ label: "Population", value: props.population.toLocaleString() });
        }

        if (metrics.length > 0) {
            popupContent += `<div style="background: #f1f5f9; border-radius: 6px; padding: 8px; margin-bottom: 8px;">`;
            metrics.forEach(m => {
                popupContent += `
                    <div style="display: flex; justify-content: space-between; font-size: 12px;">
                        <span style="color: #64748b;">${m.label}</span>
                        <span style="font-weight: 500; color: #0f172a;">${m.value}</span>
                    </div>
                `;
            });
            popupContent += `</div>`;
        }

        // Additional properties (expandable)
        const excludedKeys = ['name', 'adm0_name', 'adm1_name', 'adm2_name', 'adm3_name', 'area_sqkm', 'area', 'population',
            'geometry', 'geom', 'layer_name', 'layer_id', 'style', 'adm0_pcode', 'adm1_pcode', 'adm2_pcode', 'adm3_pcode'];
        const additionalProps = Object.entries(props).filter(([key, value]) =>
            !excludedKeys.includes(key) &&
            (typeof value === 'string' || typeof value === 'number') &&
            String(value).length < 50
        );

        if (additionalProps.length > 0) {
            popupContent += `<div style="font-size: 11px; color: #64748b; border-top: 1px solid #e2e8f0; padding-top: 6px;">`;
            additionalProps.slice(0, 5).forEach(([key, value]) => {
                const cleanKey = key.replace(/_/g, ' ').replace(/\b\w/g, l => l.toUpperCase());
                popupContent += `<div><span style="color: #94a3b8;">${cleanKey}:</span> ${value}</div>`;
            });
            popupContent += `</div>`;
        }

        popupContent += `</div>`;
        layer.bindPopup(popupContent, { maxWidth: 300 });
    }
}

export default function MapViewer({ layers, onLayerUpdate, onLayerRemove, onLayerReorder }: MapViewerProps) {
    const [showLegend, setShowLegend] = useState(true);
    const [expandedLayerId, setExpandedLayerId] = useState<string | null>(null);
    const [dismissedEmptyState, setDismissedEmptyState] = useState(false);

    // Prepare reversed layers for visual display (Top layer at top of list)
    // We reverse a copy of the ID list to determine visual order
    const reversedLayers = [...layers].reverse();

    const sensors = useSensors(
        useSensor(PointerSensor, {
            activationConstraint: {
                distance: 8,
            },
        }),
        useSensor(KeyboardSensor, {
            coordinateGetter: sortableKeyboardCoordinates,
        })
    );

    const handleDragEnd = (event: DragEndEvent) => {
        const { active, over } = event;

        if (over && active.id !== over.id) {
            const oldVisualIndex = reversedLayers.findIndex((l) => l.id === active.id);
            const newVisualIndex = reversedLayers.findIndex((l) => l.id === over.id);

            // Convert visual indices (reversed list) to original indices
            // Visual 0 -> Original LAST (length - 1)
            const fromIndex = layers.length - 1 - oldVisualIndex;
            const toIndex = layers.length - 1 - newVisualIndex;

            onLayerReorder(fromIndex, toIndex);
        }
    };

    return (
        <div className="h-full w-full relative z-0">
            <MapContainer
                center={[8.98, -79.5]} // Panama City
                zoom={8}
                scrollWheelZoom={true}
                style={{ height: "100%", width: "100%" }}
                className="z-0"
            >
                <TileLayer
                    attribution='&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
                    url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
                />

                {/* Render All Visible Layers */}
                {layers.map(layer => {
                    if (!layer.visible || !layer.data) return null;

                    // Calculate min/max for choropleth if enabled
                    let choroplethConfig: { min: number; max: number; column: string; palette: string; scale: 'linear' | 'log' } | null = null;
                    if (layer.choropleth?.enabled && layer.data.features) {
                        const { min, max } = calculateMinMax(layer.data.features, layer.choropleth.column);
                        choroplethConfig = {
                            min: layer.choropleth.min ?? min,
                            max: layer.choropleth.max ?? max,
                            column: layer.choropleth.column,
                            palette: layer.choropleth.palette,
                            scale: layer.choropleth.scale || 'linear'
                        };
                    }

                    // Create point marker function for POI layers
                    const pointToLayer = (feature: any, latlng: L.LatLng) => {
                        const props = feature.properties || {};
                        const iconChar = props.icon || layer.pointMarker?.icon;
                        const color = layer.pointMarker?.color || layer.style.color || '#6366f1';
                        const size = layer.pointMarker?.size || 24;

                        // Mode 1: Circle markers for dense data (heatmap-style)
                        // Use circles when: explicitly requested, no icon provided, or icon is "dot"/"circle"
                        if (!iconChar || iconChar === "dot" || iconChar === "circle" || layer.pointMarker?.style === "circle") {
                            return L.circleMarker(latlng, {
                                radius: size / 4 || 5,  // Convert size to radius
                                fillColor: color,
                                color: '#ffffff',
                                weight: 2,
                                opacity: 1,
                                fillOpacity: 0.7
                            });
                        }

                        // Mode 2: Icon markers for sparse, semantic features (hospitals, mountains, etc.)
                        const divIcon = L.divIcon({
                            html: `<div style="
                                font-size: ${size}px;
                                line-height: 1;
                                text-align: center;
                                filter: drop-shadow(0 2px 4px rgba(0,0,0,0.3));
                            ">${iconChar}</div>`,
                            className: 'custom-emoji-marker',
                            iconSize: [size, size],
                            iconAnchor: [size / 2, size / 2],
                            popupAnchor: [0, -size / 2]
                        });

                        return L.marker(latlng, { icon: divIcon });
                    };

                    return (
                        <GeoJSON
                            key={layer.id + JSON.stringify(layer.style) + JSON.stringify(layer.choropleth)}
                            data={layer.data}
                            pointToLayer={pointToLayer}
                            style={(feature) => {
                                // If choropleth is enabled, color by value
                                if (choroplethConfig && feature?.properties) {
                                    const value = feature.properties[choroplethConfig.column];
                                    if (typeof value === 'number') {
                                        const fillColor = getChoroplethColor(
                                            value,
                                            choroplethConfig.min,
                                            choroplethConfig.max,
                                            choroplethConfig.palette,
                                            choroplethConfig.scale
                                        );
                                        return {
                                            fillColor,
                                            fillOpacity: layer.style.fillOpacity ?? 0.7,
                                            color: layer.style.color || '#333',
                                            weight: layer.style.weight ?? 1,
                                            opacity: layer.style.opacity ?? 1
                                        };
                                    }
                                }
                                // Line styling
                                if (feature?.geometry?.type === 'LineString' || feature?.geometry?.type === 'MultiLineString') {
                                    return {
                                        color: layer.style.color,
                                        weight: layer.style.weight || 3,
                                        opacity: layer.style.opacity ?? 0.8
                                    };
                                }
                                // Default polygon style
                                return {
                                    fillColor: layer.style.fillColor ?? layer.style.color,
                                    fillOpacity: layer.style.fillOpacity ?? 0.6,
                                    color: layer.style.color,
                                    weight: layer.style.weight ?? 1,
                                    opacity: layer.style.opacity ?? 1
                                };
                            }}
                            onEachFeature={onEachFeature}
                        />
                    );
                })}

                <AutoFitBounds layers={layers} />
            </MapContainer>

            {/* Layer Control Panel */}
            {showLegend && layers.length > 0 && (
                <div className="absolute top-4 right-4 z-[1000] bg-white/95 backdrop-blur-sm rounded-xl shadow-lg border border-slate-200 p-4 w-72 max-h-[calc(100vh-2rem)] overflow-y-auto">
                    <div className="flex items-center justify-between mb-3 border-b border-slate-100 pb-2">
                        <div className="flex items-center gap-2 text-sm font-semibold text-slate-700">
                            <Layers className="w-4 h-4 text-indigo-600" />
                            Layers ({layers.length})
                        </div>
                        <button
                            onClick={() => setShowLegend(false)}
                            className="text-slate-400 hover:text-slate-600 transition-colors"
                        >
                            <X className="w-4 h-4" />
                        </button>
                    </div>

                    <DndContext
                        sensors={sensors}
                        collisionDetection={closestCenter}
                        onDragEnd={handleDragEnd}
                    >
                        <SortableContext
                            items={reversedLayers.map(l => l.id)}
                            strategy={verticalListSortingStrategy}
                        >
                            <div className="space-y-3">
                                {reversedLayers.map((layer) => (
                                    <SortableLayerItem
                                        key={layer.id}
                                        layer={layer}
                                        onUpdate={onLayerUpdate}
                                        onRemove={onLayerRemove}
                                        expandedId={expandedLayerId}
                                        setExpandedId={setExpandedLayerId}
                                    />
                                ))}
                            </div>
                        </SortableContext>
                    </DndContext>
                </div>
            )}

            {/* Toggle legend button when hidden */}
            {!showLegend && layers.length > 0 && (
                <button
                    onClick={() => setShowLegend(true)}
                    className="absolute top-4 right-4 z-[1000] bg-white/95 backdrop-blur-sm rounded-xl shadow-lg border border-slate-200 p-3 hover:bg-slate-50 transition-colors"
                    title="Show layers"
                >
                    <Layers className="w-5 h-5 text-indigo-600" />
                    <span className="ml-2 text-xs font-medium text-slate-600">{layers.length}</span>
                </button>
            )}

            {/* Empty state overlay */}
            {layers.length === 0 && !dismissedEmptyState && (
                <div className="absolute inset-0 flex items-center justify-center z-[500]">
                    <div className="bg-white/80 backdrop-blur-sm rounded-2xl shadow-lg border border-slate-200 p-6 text-center max-w-sm relative">
                        <button
                            onClick={() => setDismissedEmptyState(true)}
                            className="absolute top-2 right-2 text-slate-400 hover:text-slate-600 transition-colors p-1 rounded-lg hover:bg-slate-100"
                            title="Dismiss"
                        >
                            <X className="w-4 h-4" />
                        </button>
                        <div className="w-12 h-12 bg-indigo-100 rounded-xl flex items-center justify-center mx-auto mb-3">
                            <Layers className="w-6 h-6 text-indigo-600" />
                        </div>
                        <h3 className="font-semibold text-slate-700 mb-1">No Data Displayed</h3>
                        <p className="text-sm text-slate-500">
                            Ask GeoQuery about population, districts, or coverage to see data on the map.
                        </p>
                    </div>
                </div>
            )}
        </div>
    );
}
