import './style.css'
import L from 'leaflet'
import { EnrichedData } from './enriched_data'

// --- Types ---
interface Business {
  id: string | number;
  lat: number;
  lon: number;
  name: string;
  type: string;
  address: string;
  cp: string;
  city: string;
  phone: string;
  email: string;
  website: string;
}

// --- State ---
let map: L.Map;
let markers: L.Marker[] = [];
const cpInput = document.getElementById('cpInput') as HTMLInputElement;
const searchBtn = document.getElementById('searchBtn') as HTMLButtonElement;
const sidebar = document.getElementById('sidebar') as HTMLElement;
const businessList = document.getElementById('businessList') as HTMLElement;
const sidebarCount = document.getElementById('sidebarCount') as HTMLElement;

// --- Initialize Map ---
function initMap() {
  map = L.map('map', {
    zoomControl: false,
    attributionControl: false,
    fadeAnimation: true,
    markerZoomAnimation: true
  }).setView([40.4168, -3.7038], 6);

  L.tileLayer('https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png', {
    maxZoom: 19,
  }).addTo(map);

  L.control.zoom({ position: 'bottomright' }).addTo(map);
}

// --- Geocoding ---
async function getCoordsFromCP(cp: string): Promise<[number, number] | null> {
  try {
    const res = await fetch(`https://nominatim.openstreetmap.org/search?postalcode=${cp}&country=Spain&format=json`);
    const data = await res.json();
    if (data && data.length > 0) return [parseFloat(data[0].lat), parseFloat(data[0].lon)];
    return null;
  } catch {
    return null;
  }
}

// --- Wikidata SPARQL: Get phone/website for businesses in a CP ---
async function getWikidataForCP(cp: string): Promise<Map<string, { phone: string; website: string }>> {
  const sparql = `
    SELECT DISTINCT ?name ?phone ?website WHERE {
      ?item wdt:P17 wd:Q29 .
      ?item wdt:P281 "${cp}" .
      ?item rdfs:label ?name FILTER(LANG(?name) IN ("es","ca","en")) .
      OPTIONAL { ?item wdt:P1329 ?phone }
      OPTIONAL { ?item wdt:P856 ?website }
      FILTER(BOUND(?phone) || BOUND(?website))
    }
    LIMIT 200
  `;
  const url = `https://query.wikidata.org/sparql?query=${encodeURIComponent(sparql)}&format=json`;

  const result = new Map<string, { phone: string; website: string }>();
  try {
    const res = await fetch(url, { headers: { 'Accept': 'application/sparql-results+json' } });
    const data = await res.json();
    for (const row of data.results?.bindings || []) {
      const name = row.name?.value?.toLowerCase().trim() || '';
      if (name) {
        result.set(name, {
          phone: row.phone?.value || '',
          website: row.website?.value || ''
        });
      }
    }
  } catch (e) {
    console.warn('Wikidata query failed', e);
  }
  return result;
}

// --- Overpass: Get business locations ---
async function getBusinessesFromOverpass(lat: number, lon: number, cp: string): Promise<Business[]> {
  const areaQuery = `
    [out:json][timeout:30];
    area["addr:postcode"="${cp}"]["addr:country"="ES"]->.a;
    (
      node["name"]["shop"](area.a);
      node["name"]["amenity"~"restaurant|cafe|bar|bank|pharmacy|hospital|cinema|hotel|fast_food|pub"](area.a);
      node["name"]["office"](area.a);
      node["name"]["craft"](area.a);
      node["name"]["tourism"~"hotel|museum|gallery|attraction"](area.a);
      way["name"]["shop"](area.a);
      way["name"]["amenity"~"restaurant|cafe|bar|bank|pharmacy|hospital|cinema|hotel|fast_food"](area.a);
      way["name"]["tourism"~"hotel|museum|gallery|attraction"](area.a);
    );
    out body center;
  `;
  const radialQuery = `
    [out:json][timeout:25];
    (
      node["name"]["shop"](around:1500,${lat},${lon});
      node["name"]["amenity"~"restaurant|cafe|bar|bank|pharmacy|hospital|cinema|hotel|fast_food"](around:1500,${lat},${lon});
      node["name"]["office"](around:1500,${lat},${lon});
      way["name"]["shop"](around:1500,${lat},${lon});
      way["name"]["amenity"~"restaurant|cafe|bar|bank|pharmacy|hospital|cinema|hotel"](around:1500,${lat},${lon});
    );
    out body center;
  `;

  const run = async (q: string) => {
    const r = await fetch(`https://overpass-api.de/api/interpreter?data=${encodeURIComponent(q)}`);
    return r.json();
  };

  try {
    let data = await run(areaQuery).catch(() => ({ elements: [] }));
    if (!data.elements || data.elements.length === 0) data = await run(radialQuery);

    return data.elements.map((el: any) => {
      const t = el.tags || {};
      const name = t.name || t.brand || t.operator || "Negocio sin nombre";
      const type = t.shop || t.amenity || t.office || t.craft || t.tourism || "Empresa";
      const address = `${t["addr:street"] || ""} ${t["addr:housenumber"] || ""}`.trim() || "Dirección no disponible";
      const city = t["addr:city"] || t["addr:suburb"] || t["addr:place"] || "N/A";
      return {
        id: el.id,
        lat: el.lat || el.center?.lat,
        lon: el.lon || el.center?.lon,
        name, type, address,
        cp: t["addr:postcode"] || t["addr:postal_code"] || cp,
        city,
        phone: t.phone || t["contact:phone"] || "",
        email: t.email || t["contact:email"] || "",
        website: t.website || t["contact:website"] || ""
      };
    }).filter((b: any) => b.lat && b.lon);
  } catch {
    return [];
  }
}

// --- Fuzzy name match helper ---
function fuzzyMatch(a: string, b: string): boolean {
  const normalize = (s: string) => s.toLowerCase().replace(/[^a-záéíóúüñ0-9]/gi, '');
  const na = normalize(a), nb = normalize(b);
  return na === nb || na.includes(nb.substring(0, Math.min(nb.length, 12))) || nb.includes(na.substring(0, Math.min(na.length, 12)));
}

// --- Main: fetch and enrich ---
async function getNearbyBusinesses(lat: number, lon: number, cp: string): Promise<Business[]> {
  // Run Overpass + Wikidata in parallel
  const [overpassResults, wikidataMap] = await Promise.all([
    getBusinessesFromOverpass(lat, lon, cp),
    getWikidataForCP(cp)
  ]);

  // Enrich Overpass results with Wikidata data
  const enrichedFromAPI: Business[] = overpassResults.map(b => {
    // Check Wikidata match
    for (const [wdName, wdData] of wikidataMap.entries()) {
      if (fuzzyMatch(b.name, wdName)) {
        return {
          ...b,
          phone: b.phone || wdData.phone,
          website: b.website || wdData.website
        };
      }
    }
    return b;
  });

  // Priority: manual enriched > enriched API data > raw API data
  const manualEnriched = EnrichedData[cp] || [];
  const combined = [...manualEnriched, ...enrichedFromAPI];

  // O(N) dedup
  const seen = new Map<string, Business>();
  combined.forEach(b => {
    const key = `${b.name.toLowerCase()}_${b.lat.toFixed(4)}_${b.lon.toFixed(4)}`;
    if (!seen.has(key)) seen.set(key, b);
  });

  return Array.from(seen.values()).slice(0, 200);
}

// --- Render ---
function clearMarkers() {
  markers.forEach(m => map.removeLayer(m));
  markers = [];
}

function renderBusinesses(businesses: Business[]) {
  businessList.innerHTML = '';
  sidebarCount.innerText = `${businesses.length} resultados`;

  if (businesses.length === 0) {
    sidebar.classList.remove('open');
    return;
  }
  sidebar.classList.add('open');

  businesses.forEach(b => {
    const searchQuery = encodeURIComponent(`${b.name} ${b.address !== "Dirección no disponible" ? b.address : ""} ${b.city !== "N/A" ? b.city : ""}`);
    const googleSearchUrl = `https://www.google.com/search?q=${searchQuery}`;

    // Popup content
    const phoneHtml = b.phone ? `<b>Tel:</b> ${b.phone}<br>` : '';
    const emailHtml = b.email ? `<b>Mail:</b> ${b.email}<br>` : '';
    const webHtml = b.website
      ? `<b>Web:</b> <a href="${b.website}" target="_blank" style="color:#818cf8;">${b.website}</a>`
      : `<span style="color:#94a3b8;font-style:italic;font-size:0.8rem;">No dispone de página web</span>`;

    const popupContent = `
      <div style="font-family:'Inter',sans-serif;color:#f8fafc;min-width:220px;">
        <strong style="color:#6366f1;font-size:1.05rem;">${b.name}</strong><br>
        <span style="font-size:0.75rem;color:#94a3b8;text-transform:capitalize;">${b.type}</span>
        <hr style="border:0;border-top:1px solid rgba(255,255,255,0.1);margin:7px 0;">
        <div style="font-size:0.82rem;line-height:1.6;">
          <b>Dir:</b> ${b.address}<br>
          <b>CP/Pob:</b> ${b.cp} / ${b.city}<br>
          ${phoneHtml}${emailHtml}${webHtml}
        </div>
      </div>
    `;

    const marker = L.marker([b.lat, b.lon]).addTo(map).bindPopup(popupContent);
    markers.push(marker);

    // Sidebar row helper
    const infoRow = (label: string, value: string) => {
      if (value) return `<div class="info-row"><span class="info-label">${label}:</span> <span>${value}</span></div>`;
      return `
        <div class="info-row">
          <span class="info-label">${label}:</span>
          <a href="${googleSearchUrl}" target="_blank" class="search-fallback-link">
            <span class="search-icon">🔍</span> Buscar ${label.toLowerCase()}
          </a>
        </div>`;
    };

    const websiteHtml = b.website
      ? `<a href="${b.website}" target="_blank" class="website-link">${b.website}</a>`
      : `<span style="color:#94a3b8;font-size:0.85rem;font-style:italic;">No dispone de página web</span>
         <a href="${googleSearchUrl}" target="_blank" class="search-fallback-link" style="margin-left:8px;"><span class="search-icon">🔍</span></a>`;

    const item = document.createElement('div');
    item.className = 'business-item';
    item.innerHTML = `
      <div class="business-name">${b.name}</div>
      <div class="business-info">
        <div class="info-row"><span class="info-label">Tipo:</span> <span style="text-transform:capitalize;">${b.type}</span></div>
        <div class="info-row"><span class="info-label">Dir:</span> <span>${b.address}</span></div>
        <div class="info-row"><span class="info-label">CP/Pob:</span> <span>${b.cp} / ${b.city}</span></div>
        ${infoRow("Tel", b.phone)}
        ${infoRow("Mail", b.email)}
        <div class="info-row"><span class="info-label">Web:</span> ${websiteHtml}</div>
      </div>
    `;
    item.onclick = (e) => {
      if ((e.target as HTMLElement).tagName === 'A' || (e.target as HTMLElement).closest('a')) return;
      map.setView([b.lat, b.lon], 18);
      marker.openPopup();
    };
    businessList.appendChild(item);
  });
}

// --- Search ---
async function handleSearch() {
  const cp = cpInput.value.trim();
  if (!/^\d{5}$/.test(cp)) {
    alert("Por favor, introduce un código postal válido de 5 dígitos.");
    return;
  }

  searchBtn.disabled = true;
  searchBtn.innerText = "Buscando...";
  sidebar.classList.remove('open');
  clearMarkers();

  const coords = await getCoordsFromCP(cp);
  if (coords) {
    map.setView(coords, 15);
    const businesses = await getNearbyBusinesses(coords[0], coords[1], cp);
    renderBusinesses(businesses);
  } else {
    alert("No se ha podido encontrar la ubicación para este código postal.");
  }

  searchBtn.disabled = false;
  searchBtn.innerText = "Buscar";
}

// --- Events ---
searchBtn.addEventListener('click', handleSearch);
cpInput.addEventListener('keypress', (e) => { if (e.key === 'Enter') handleSearch(); });

// Start
initMap();
