// src/components/MapaBsAs.tsx
import { useState, useMemo, useCallback, useEffect } from 'react';
import { ComposableMap, Geographies, Geography, ZoomableGroup } from 'react-simple-maps';
import { Tooltip } from 'react-tooltip';
import { useQuery } from '@tanstack/react-query';
import axios from 'axios';
import type { Feature, Geometry } from 'geojson';
import { geoCentroid } from 'd3-geo';
import './MapaBsAs.css';

// --- Interfaces y Tipos ---
type PointTuple = [number, number];

interface ResultadoMapa {
  ambitoId: number;
  departamentoNombre: string;
  agrupacionGanadoraId: string;
  colorGanador: string | null;
}

interface ResultadoDetalladoMunicipio {
  municipioNombre: string;
  ultimaActualizacion: string;
  porcentajeEscrutado: number;
  porcentajeParticipacion: number;
  resultados: { id: string; nombre: string; votos: number; porcentaje: number; color: string | null; }[];
  votosAdicionales: { enBlanco: number; nulos: number; recurridos: number };
}

interface Agrupacion {
  id: string;
  nombre: string;
}

interface Categoria {
  id: number;
  nombre: string;
}

interface PartidoProperties {
  departamento: string;
}

type PartidoGeography = Feature<Geometry, PartidoProperties> & { rsmKey: string };

// --- Constantes ---
const API_BASE_URL = 'http://localhost:5217/api';
const MIN_ZOOM = 1;
const MAX_ZOOM = 8;
const TRANSLATE_EXTENT: [[number, number], [number, number]] = [[-100, -1000], [1100, 800]];
const INITIAL_POSITION = { center: [-60.5, -37.2] as PointTuple, zoom: MIN_ZOOM };
const DEFAULT_MAP_COLOR = '#E0E0E0';

const CATEGORIAS: Categoria[] = [
  { id: 5, nombre: 'Senadores' },
  { id: 6, nombre: 'Diputados' },
  { id: 7, nombre: 'Concejales' }
];

// --- Componente Principal ---
const MapaBsAs = () => {
  const [position, setPosition] = useState(INITIAL_POSITION);
  const [selectedAmbitoId, setSelectedAmbitoId] = useState<number | null>(null);
  const [selectedCategoriaId, setSelectedCategoriaId] = useState<number>(6);
  const [tooltipContent, setTooltipContent] = useState('');
  const [isPanning, setIsPanning] = useState(false);

  const { data: resultadosData, isLoading: isLoadingResultados } = useQuery<ResultadoMapa[]>({
    queryKey: ['mapaResultadosPorMunicipio', selectedCategoriaId],
    queryFn: async () => (await axios.get(`${API_BASE_URL}/Resultados/mapa-por-municipio?categoriaId=${selectedCategoriaId}`)).data,
  });

  const { data: geoData, isLoading: isLoadingGeo } = useQuery<any>({
    queryKey: ['mapaGeoData'],
    queryFn: async () => (await axios.get('/partidos-bsas.topojson')).data,
  });

  const { data: agrupacionesData, isLoading: isLoadingAgrupaciones } = useQuery<Agrupacion[]>({
    queryKey: ['catalogoAgrupaciones'],
    queryFn: async () => (await axios.get(`${API_BASE_URL}/Catalogos/agrupaciones`)).data,
  });

  const { nombresAgrupaciones, resultadosPorDepartamento } = useMemo<{
    nombresAgrupaciones: Map<string, string>;
    resultadosPorDepartamento: Map<string, ResultadoMapa>;
  }>(() => {
    const nombresMap = new Map<string, string>();
    const resultadosMap = new Map<string, ResultadoMapa>();
    if (agrupacionesData) {
      agrupacionesData.forEach((agrupacion) => {
        nombresMap.set(agrupacion.id, agrupacion.nombre);
      });
    }
    if (resultadosData) {
      resultadosData.forEach(r => {
        if (r.departamentoNombre) {
          resultadosMap.set(r.departamentoNombre.toUpperCase(), r)
        }
      });
    }
    return { nombresAgrupaciones: nombresMap, resultadosPorDepartamento: resultadosMap };
  }, [agrupacionesData, resultadosData]);

  const isLoading = isLoadingResultados || isLoadingAgrupaciones || isLoadingGeo;

  const handleReset = useCallback(() => {
    setSelectedAmbitoId(null);
    setPosition(INITIAL_POSITION);
  }, []);

  const handleGeographyClick = useCallback((geo: PartidoGeography) => {
    const departamentoNombre = geo.properties.departamento.toUpperCase();
    const resultado = resultadosPorDepartamento.get(departamentoNombre);
    if (!resultado) return;
    const ambitoIdParaSeleccionar = resultado.ambitoId;
    if (selectedAmbitoId === ambitoIdParaSeleccionar) {
      handleReset();
    } else {
      const centroid = geoCentroid(geo) as PointTuple;
      setPosition({ center: centroid, zoom: 5 });
      setSelectedAmbitoId(ambitoIdParaSeleccionar);
    }
  }, [selectedAmbitoId, handleReset, resultadosPorDepartamento]);

  const handleMoveEnd = (newPosition: { coordinates: PointTuple; zoom: number }) => { // <--- CORRECCIÓN 1
    if (newPosition.zoom <= MIN_ZOOM) {
      if (position.zoom > MIN_ZOOM || selectedAmbitoId !== null) {
        handleReset();
      }
      return;
    }
    if (newPosition.zoom < position.zoom && selectedAmbitoId !== null) {
      setSelectedAmbitoId(null);
    }
    setPosition({ center: newPosition.coordinates, zoom: newPosition.zoom });
  };

  const handleZoomIn = () => {
    if (position.zoom < MAX_ZOOM) {
      setPosition(p => ({ ...p, zoom: Math.min(p.zoom * 1.5, MAX_ZOOM) }));
    }
  };

  useEffect(() => {
    const handleKeyDown = (e: KeyboardEvent) => e.key === 'Escape' && handleReset();
    window.addEventListener('keydown', handleKeyDown);
    return () => window.removeEventListener('keydown', handleKeyDown);
  }, [handleReset]);

  const getPartyFillColor = (departamentoNombre: string) => {
    const resultado = resultadosPorDepartamento.get(departamentoNombre.toUpperCase());
    return resultado?.colorGanador || DEFAULT_MAP_COLOR;
  };

  // --- Helper de Renderizado ---
  const renderGeography = (geo: PartidoGeography, isSelectedGeo: boolean = false) => {
    const departamentoNombre = geo.properties.departamento.toUpperCase();
    const resultado = resultadosPorDepartamento.get(departamentoNombre);
    const isClickable = !!resultado;
    const isSelected = isSelectedGeo || (selectedAmbitoId !== null && selectedAmbitoId === resultado?.ambitoId);
    const isFaded = !isSelectedGeo && selectedAmbitoId !== null && !isSelected;
    const nombreAgrupacionGanadora = resultado ? nombresAgrupaciones.get(resultado.agrupacionGanadoraId) : 'Sin datos';

    return (
      <Geography
        key={geo.rsmKey + (isSelectedGeo ? '-selected' : '')}
        geography={geo}
        data-tooltip-id="partido-tooltip"
        data-tooltip-content={`${geo.properties.departamento}: ${nombreAgrupacionGanadora}`}
        className={`rsm-geography ${isSelected ? 'selected' : ''} ${isFaded ? 'faded' : ''} ${!isClickable ? 'no-results' : ''}`}
        fill={getPartyFillColor(geo.properties.departamento)}
        onClick={isClickable ? () => handleGeographyClick(geo) : undefined}
        onMouseEnter={() => setTooltipContent(`${geo.properties.departamento}: ${nombreAgrupacionGanadora}`)}
        onMouseLeave={() => setTooltipContent("")}
      />
    );
  };

  return (
    <div className="mapa-wrapper">
      <div className="mapa-container">
        {isLoading ? <div className="spinner"></div> : (
          <ComposableMap
            key={selectedCategoriaId}
            projection="geoMercator"
            projectionConfig={{ scale: 4400, center: [-60.5, -37.2] }}
            className="rsm-svg"
            data-tooltip-id="partido-tooltip"
          >
            <ZoomableGroup
              center={position.center}
              zoom={position.zoom}
              className={isPanning ? 'panning' : ''}
              onMoveStart={() => setIsPanning(true)}
              onMoveEnd={(newPosition: { coordinates: PointTuple; zoom: number }) => {
                setIsPanning(false);
                handleMoveEnd(newPosition);
              }}
              translateExtent={TRANSLATE_EXTENT}
              minZoom={MIN_ZOOM}
              maxZoom={MAX_ZOOM}
              filterZoomEvent={(e: WheelEvent) => {
                if (e.deltaY > 0) {
                  handleReset();
                } else if (e.deltaY < 0) {
                  handleZoomIn();
                }
                return true;
              }}
            >
              {geoData && (
                <Geographies geography={geoData}>
                  {({ geographies }: { geographies: PartidoGeography[] }) => {
                    const selectedGeo = selectedAmbitoId
                      ? geographies.find(geo => {
                        const resultado = resultadosPorDepartamento.get(geo.properties.departamento.toUpperCase());
                        return resultado?.ambitoId === selectedAmbitoId;
                      })
                      : null;

                    return (
                      <>
                        {geographies.map(geo => (!selectedGeo || geo.rsmKey !== selectedGeo.rsmKey) ? renderGeography(geo) : null)}
                        {selectedGeo && renderGeography(selectedGeo, true)}
                      </>
                    );
                  }}
                </Geographies>
              )}
            </ZoomableGroup>
          </ComposableMap>
        )}
        <Tooltip id="partido-tooltip" content={tooltipContent} />
        {selectedAmbitoId !== null && <ControlesMapa onReset={handleReset} />}
      </div>
      <div className="info-panel">
        <div className="mapa-categoria-selector">
          <select
            className="mapa-categoria-combobox"
            value={selectedCategoriaId}
            onChange={(e) => {
              setSelectedCategoriaId(Number(e.target.value));
              handleReset();
            }}
          >
            {CATEGORIAS.map(cat => (
              <option key={cat.id} value={cat.id}>
                {cat.nombre}
              </option>
            ))}
          </select>
        </div>
        <DetalleMunicipio ambitoId={selectedAmbitoId} onReset={handleReset} categoriaId={selectedCategoriaId} />
        <Legend resultados={resultadosPorDepartamento} nombresAgrupaciones={nombresAgrupaciones} />
      </div>
    </div>
  );
};

// --- Sub-componentes ---
const ControlesMapa = ({ onReset }: { onReset: () => void }) => (
  <div className="map-controls">
    <button onClick={onReset}>← VOLVER</button>
  </div>
);

const DetalleMunicipio = ({ ambitoId, onReset, categoriaId }: { ambitoId: number | null; onReset: () => void; categoriaId: number; }) => {
  const { data, isLoading, error } = useQuery<ResultadoDetalladoMunicipio>({
    queryKey: ['municipioDetalle', ambitoId, categoriaId],
    queryFn: async () => (await axios.get(`${API_BASE_URL}/Resultados/municipio/${ambitoId}?categoriaId=${categoriaId}`)).data,
    enabled: !!ambitoId,
  });

  if (!ambitoId) return (<div className="detalle-placeholder"><h3>Provincia de Buenos Aires</h3><p>Seleccione un municipio en el mapa para ver los resultados detallados.</p></div>);
  if (isLoading) return (<div className="detalle-loading"><div className="spinner"></div><p>Cargando resultados...</p></div>);
  if (error) return <div className="detalle-error">Error al cargar los datos del municipio.</div>;

  return (
    <div className="detalle-content">
      <button className="reset-button-panel" onClick={onReset}>← VOLVER</button>
      <h3>{data?.municipioNombre}</h3>
      <div className="detalle-metricas">
        <span><strong>Escrutado:</strong> {(data?.porcentajeEscrutado ?? 0).toFixed(2)}%</span>
        <span><strong>Participación:</strong> {(data?.porcentajeParticipacion ?? 0).toFixed(2)}%</span>
      </div>
      <ul className="resultados-lista">
        {(data?.resultados ?? []).map((r, index) => (
          <li key={`${r.nombre}-${index}`}>
            <div className="resultado-info">
              <span className="partido-nombre">{r.nombre}</span>
              <span className="partido-votos">{r.votos.toLocaleString('es-AR')} ({r.porcentaje.toFixed(2)}%)</span>
            </div>
            <div className="progress-bar">
              <div
                className="progress-fill"
                style={{
                  width: `${r.porcentaje}%`,
                  backgroundColor: r.color || DEFAULT_MAP_COLOR
                }}
              ></div>
            </div>
          </li>
        ))}
      </ul>
    </div>
  );
};

const Legend = ({ resultados, nombresAgrupaciones }: { resultados: Map<string, ResultadoMapa>, nombresAgrupaciones: Map<string, string> }) => {

  const legendItems = useMemo(() => {
    const ganadoresUnicos = new Map<string, { nombre: string; color: string }>();

    resultados.forEach(resultado => {
      if (resultado.colorGanador && !ganadoresUnicos.has(resultado.agrupacionGanadoraId)) {
        ganadoresUnicos.set(resultado.agrupacionGanadoraId, {
          nombre: nombresAgrupaciones.get(resultado.agrupacionGanadoraId) || 'Desconocido',
          color: resultado.colorGanador
        });
      }
    });

    return Array.from(ganadoresUnicos.values());
  }, [resultados, nombresAgrupaciones]);

  return (
    <div className="legend">
      <h4>Leyenda de Ganadores</h4>
      {legendItems.map(item => (
        <div key={item.nombre} className="legend-item">
          <div className="legend-color-box" style={{ backgroundColor: item.color }} />
          <span>{item.nombre}</span>
        </div>
      ))}
    </div>
  );
};

export default MapaBsAs;