// src/features/legislativas/provinciales/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 { feature } from 'topojson-client';
import type { Feature, Geometry } from 'geojson';
import { geoCentroid } from 'd3-geo';
import { API_BASE_URL, assetBaseUrl } from '../../../apiService';
import './MapaBsAs.css';

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

interface MapaBsAsProps {
  focoMunicipio?: string;
  focoCategoria?: string;
}

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 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: 6, nombre: 'Diputados' },
  { id: 5, nombre: 'Senadores' },
  { id: 7, nombre: 'Concejales' }
];

// --- Helper de Normalización ---
const normalizarTexto = (texto: string = ''): string => {
  return texto
    .trim()
    .toUpperCase()
    .normalize("NFD") // Separa los acentos de las letras
    .replace(/[\u0300-\u036f]/g, ""); // Elimina los acentos
};

// --- Componente Principal ---
const MapaBsAs = ({ focoMunicipio, focoCategoria }: MapaBsAsProps) => {
  // --- LÓGICA DE ESTADO SIMPLIFICADA ---
  const categoriaInicial = useMemo(() => {
    const catNorm = focoCategoria?.toLowerCase();
    if (catNorm === 'senadores') return 5;
    if (catNorm === 'concejales') return 7;
    return 6;
  }, [focoCategoria]);

  const [position, setPosition] = useState(INITIAL_POSITION);
  const [selectedAmbitoId, setSelectedAmbitoId] = useState<number | null>(null);
  const [selectedCategoriaId, setSelectedCategoriaId] = useState<number>(categoriaInicial);
  const [tooltipContent, setTooltipContent] = useState('');
  const [isPanning, setIsPanning] = useState(false);

  // Sincroniza el estado si la prop cambia. Esto es para cuando el widget ya está montado
  // y recibe nuevas props (no ocurrirá en tu caso actual, pero es buena práctica).
  useEffect(() => {
    setSelectedCategoriaId(categoriaInicial);
  }, [categoriaInicial]);

  // --- QUERIES ---
  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(`${assetBaseUrl}/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(() => {
    const nombresMap = new Map<string, string>();
    const resultadosMap = new Map<string, ResultadoMapa>();
    if (agrupacionesData) {
      agrupacionesData.forEach((a) => nombresMap.set(a.id, a.nombre));
    }
    if (resultadosData) {
      resultadosData.forEach(r => {
        if (r.departamentoNombre) {
          resultadosMap.set(normalizarTexto(r.departamentoNombre), r);
        }
      });
    }
    return { nombresAgrupaciones: nombresMap, resultadosPorDepartamento: resultadosMap };
  }, [agrupacionesData, resultadosData]);

  const isLoading = isLoadingResultados || isLoadingAgrupaciones || isLoadingGeo;

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

  // --- LÓGICA DE CLIC Y FOCO ---
  const handleGeographyClick = useCallback((geo: PartidoGeography) => {
    const departamentoNombreNormalizado = normalizarTexto(geo.properties.departamento);
    const resultado = resultadosPorDepartamento.get(departamentoNombreNormalizado);
    if (!resultado) return;

    if (selectedAmbitoId === resultado.ambitoId) {
      handleReset();
    } else {
      const centroid = geoCentroid(geo) as PointTuple;
      setPosition({ center: centroid, zoom: 5 });
      setSelectedAmbitoId(resultado.ambitoId);
    }
  }, [selectedAmbitoId, handleReset, resultadosPorDepartamento]);

  // --- useEffect DE INICIALIZACIÓN ---
  useEffect(() => {
    if (isLoading || !focoMunicipio || selectedAmbitoId) {
      return;
    }

    const geometries = geoData?.objects?.['departamentos-buenos_aires']?.geometries;
    if (!geometries) return;

    const nombreFocoNormalizado = normalizarTexto(focoMunicipio);
    const geoTargetTopo = geometries.find(
      (g: any) => normalizarTexto(g.properties.departamento) === nombreFocoNormalizado
    );

    if (geoTargetTopo) {
      if (resultadosPorDepartamento.has(nombreFocoNormalizado)) {
        const geoTargetGeoJSON = feature(geoData, geoTargetTopo);
        handleGeographyClick(geoTargetGeoJSON as unknown as PartidoGeography);
      }
    }
    // Deshabilitamos la regla para que solo se ejecute cuando isLoading cambia a false.
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [isLoading]);

  useEffect(() => {
    if (categoriaInicial !== selectedCategoriaId) {
      setSelectedCategoriaId(categoriaInicial);
      handleReset();
    }
  }, [categoriaInicial, selectedCategoriaId, handleReset]);

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

  const renderGeography = useCallback((geo: PartidoGeography, isSelectedGeo: boolean = false) => {
    const departamentoNombreNormalizado = normalizarTexto(geo.properties.departamento);
    const resultado = resultadosPorDepartamento.get(departamentoNombreNormalizado);
    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}
        className={`rsm-geography ${isSelected ? 'selected' : ''} ${isFaded ? 'faded' : ''} ${!isClickable ? 'no-results' : ''}`}
        fill={resultado?.colorGanador || DEFAULT_MAP_COLOR}
        onClick={isClickable ? () => handleGeographyClick(geo) : undefined}
        onMouseEnter={() => {
          if (isClickable) {
            setTooltipContent(`${geo.properties.departamento}: ${nombreAgrupacionGanadora}`);
          }
        }}
        onMouseLeave={() => setTooltipContent("")}
      />
    );
  }, [resultadosPorDepartamento, selectedAmbitoId, nombresAgrupaciones, handleGeographyClick]);

  useEffect(() => {
    if (isLoading || !focoMunicipio || selectedAmbitoId) {
      return;
    }

    const geometries = geoData?.objects?.['departamentos-buenos_aires']?.geometries;
    if (!geometries) return;

    const nombreFocoNormalizado = normalizarTexto(focoMunicipio);
    const geoTargetTopo = geometries.find(
      (g: any) => normalizarTexto(g.properties.departamento) === nombreFocoNormalizado
    );

    if (geoTargetTopo && resultadosPorDepartamento.has(nombreFocoNormalizado)) {
      const geoTargetGeoJSON = feature(geoData, geoTargetTopo);
      handleGeographyClick(geoTargetGeoJSON as unknown as PartidoGeography);
    }
  }, [isLoading, focoMunicipio, selectedCategoriaId]);

  const handleMoveEnd = (newPosition: { coordinates: PointTuple; zoom: number }) => {
    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]);

  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(normalizarTexto(geo.properties.departamento));
                        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) => {
              // --- LÓGICA DE CAMBIO DE CATEGORÍA ---
              // Limpiamos el foco de municipio al cambiar de categoría
              setSelectedAmbitoId(null);
              setPosition(INITIAL_POSITION);
              setSelectedCategoriaId(Number(e.target.value));
            }}
          >
            {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;