/* =============================================================
   Nærmeste leveringssteder for EE-avfall.
   Data fra OpenStreetMap (Overpass), stedssøk via Nominatim.
   Ingen nøkler, ingen kostnad, ingen sporing.
   Posisjon hentes kun når brukeren selv ber om det.
   ============================================================= */

/* Overpass svarer ikke alltid. Uten hard grense per kall kan
   forespørselen bli hengende i minutter. */
async function hentMedFrist(url, opsjoner, ms) {
  const ctrl = new AbortController();
  const t = setTimeout(() => ctrl.abort(), ms);
  try { return await fetch(url, { ...opsjoner, signal: ctrl.signal }); }
  finally { clearTimeout(t); }
}

function avstandKm(a, b, c, d) {
  const R = 6371, r = Math.PI / 180;
  const dLat = (c - a) * r, dLon = (d - b) * r;
  const x = Math.sin(dLat/2) ** 2 +
            Math.cos(a * r) * Math.cos(c * r) * Math.sin(dLon/2) ** 2;
  return R * 2 * Math.atan2(Math.sqrt(x), Math.sqrt(1 - x));
}

/* Store kjeder har egne mottaksordninger og forutsigbare rutiner.
   En liten lokal databutikk plikter formelt det samme, men i praksis
   er kjedene et tryggere svar å gi en kunde. */
const KJEDER = [
  "elkjøp", "elkjop", "power", "expert", "elon", "komplett", "skousen",
  "euronics", "multicom", "lefdal", "clas ohlson", "biltema", "jula",
  "kjell & company", "kjell og company", "kjell&company", "eplehuset", "obs",
];

function erKjede(navn, merke) {
  const t = ((merke || "") + " " + (navn || "")).toLowerCase();
  return KJEDER.some(k => t.includes(k));
}

const stasjonBuffer = new Map();

async function finnStasjoner(lat, lon) {
  const nokkel = lat.toFixed(2) + "," + lon.toFixed(2);
  if (stasjonBuffer.has(nokkel)) return stasjonBuffer.get(nokkel);
  const sok = async (radius) => {
    /* Elektrobutikker er tatt med med vilje: avfallsforskriften pålegger
       forhandlere å ta imot EE-avfall vederlagsfritt. For småting er
       nærmeste butikk ofte et bedre svar enn gjenvinningsstasjonen. */
    const q = `[out:json][timeout:25];
(
  nwr["amenity"="recycling"]["recycling_type"="centre"](around:${radius},${lat},${lon});
  nwr["amenity"="recycling"]["recycling:electrical_appliances"="yes"](around:${radius},${lat},${lon});
  nwr["amenity"="recycling"]["recycling:small_appliances"="yes"](around:${radius},${lat},${lon});
  nwr["amenity"="waste_transfer_station"](around:${radius},${lat},${lon});
  nwr["shop"="electronics"](around:${radius},${lat},${lon});
  nwr["shop"="computer"](around:${radius},${lat},${lon});
  nwr["shop"="appliance"](around:${radius},${lat},${lon});
);
out center 120;`;
    /* Den offentlige Overpass-instansen svarer bare rundt halvparten av
       gangene (406/504 under last). Derfor flere speil OG flere forsøk. */
    const speil = [
      "https://overpass-api.de/api/interpreter",
      "https://overpass.kumi.systems/api/interpreter",
      "https://overpass.private.coffee/api/interpreter",
    ];
    let d = null, sisteFeil = null;
    for (let i = 0; i < speil.length; i++) {
      try {
        const r = await hentMedFrist(speil[i], {
          method: "POST",
          headers: { "Content-Type": "text/plain;charset=UTF-8" },
          body: q,
        }, 9000);
        if (!r.ok) { sisteFeil = "HTTP " + r.status; }
        else { d = await r.json(); break; }
      } catch (e) { sisteFeil = String(e); }
      if (i < speil.length - 1) await new Promise(r => setTimeout(r, 500));
    }
    if (!d) throw new Error("overpass: " + sisteFeil);
    return (d.elements || []).map(e => {
      const la = e.lat ?? e.center?.lat, lo = e.lon ?? e.center?.lon;
      if (la == null || lo == null) return null;
      const t = e.tags || {};
      const butikk = !!t.shop;
      const navn = t.name || t.brand || t.operator || (butikk ? "Elektrobutikk" : "Gjenvinningsstasjon");
      const kjede = butikk && (erKjede(navn, t.brand) || !!t["brand:wikidata"]);
      return {
        id: e.type + e.id,
        type: butikk ? (kjede ? "kjede" : "butikk") : "stasjon",
        navn,
        drift: t.operator && t.operator !== t.name ? t.operator : null,
        adresse: [t["addr:street"], t["addr:housenumber"]].filter(Boolean).join(" ") || null,
        sted: t["addr:city"] || t["addr:place"] || null,
        aapent: t.opening_hours || null,
        lat: la, lon: lo,
        km: avstandKm(lat, lon, la, lo),
      };
    }).filter(Boolean);
  };

  let treff = await sok(15000);
  if (treff.length < 4) treff = await sok(60000);
  treff = treff.filter(s => !(s.type === "butikk" && s.navn === "Elektrobutikk"));

  const sett = new Map();
  treff.forEach(s => { const n = s.navn + Math.round(s.km * 10); if (!sett.has(n)) sett.set(n, s); });
  const sortert = [...sett.values()].sort((a, b) => a.km - b.km);

  /* Prioritering: to nærmeste kjeder, én gjenvinningsstasjon.
     Små lokalbutikker fyller bare inn hvis det ikke finnes nok av de andre. */
  const kjeder    = sortert.filter(s => s.type === "kjede");
  const stasjoner = sortert.filter(s => s.type === "stasjon");
  const smaa      = sortert.filter(s => s.type === "butikk");

  const topp = [];
  kjeder.slice(0, 2).forEach(s => topp.push(s));
  if (stasjoner[0]) topp.push(stasjoner[0]);
  for (const liste of [kjeder.slice(2), stasjoner.slice(1), smaa]) {
    for (const s of liste) { if (topp.length >= 3) break; if (!topp.includes(s)) topp.push(s); }
    if (topp.length >= 3) break;
  }
  topp.length = Math.min(topp.length, 3);
  const svar = topp.sort((a, b) => a.km - b.km);
  stasjonBuffer.set(nokkel, svar);
  return svar;
}

async function finnSted(tekst) {
  const u = "https://nominatim.openstreetmap.org/search?format=json&limit=1&countrycodes=no&q="
          + encodeURIComponent(tekst);
  const r = await hentMedFrist(u, { headers: { "Accept-Language": "nb" } }, 8000);
  if (!r.ok) throw new Error("nominatim");
  const d = await r.json();
  if (!d.length) return null;
  return { lat: parseFloat(d[0].lat), lon: parseFloat(d[0].lon), navn: d[0].display_name.split(",")[0] };
}

function Eavfall() {
  const kjoerRef = useRef(0);
  const [apen, setApen]     = useState(false);
  const [laster, setLaster] = useState(false);
  const [feil, setFeil]     = useState("");
  const [steder, setSteder] = useState(null);
  const [sted, setSted]     = useState("");
  const [fra, setFra]       = useState("");

  const hent = async (lat, lon, kilde) => {
    const min = ++kjoerRef.current;
    setLaster(true); setFeil(""); setSteder(null);
    try {
      const s = await finnStasjoner(lat, lon);
      if (min !== kjoerRef.current) return;          // et nyere søk har overtatt
      if (!s.length) setFeil("Fant ingen registrerte mottak i nærheten. Prøv en større by.");
      else { setSteder(s); setFra(kilde); }
    } catch {
      if (min !== kjoerRef.current) return;
      setFeil("Kartdatabasen svarte ikke. Den er åpen og litt ustabil — prøv en gang til.");
    } finally {
      if (min === kjoerRef.current) setLaster(false);
    }
  };

  const brukPosisjon = () => {
    if (!navigator.geolocation) { setFeil("Nettleseren din støtter ikke posisjon. Skriv inn stedet i stedet."); return; }
    setLaster(true); setFeil("");
    navigator.geolocation.getCurrentPosition(
      p => hent(p.coords.latitude, p.coords.longitude, "din posisjon"),
      () => { setLaster(false); setFeil("Fikk ikke tilgang til posisjon. Skriv inn stedet i stedet."); },
      { timeout: 12000, maximumAge: 300000 }
    );
  };

  const sokSted = async () => {
    const t = sted.trim();
    if (t.length < 2) { setFeil("Skriv inn et sted."); return; }
    setLaster(true); setFeil(""); setSteder(null);
    try {
      const p = await finnSted(t);
      if (!p) { setFeil("Fant ikke stedet. Prøv et postnummer eller en kommune."); setLaster(false); return; }
      await hent(p.lat, p.lon, p.navn);
    } catch {
      setFeil("Stedssøket svarte ikke. Prøv igjen om litt.");
      setLaster(false);
    }
  };

  if (!apen) return (
    <button className="sl-eavfall__utloser" onClick={() => setApen(true)}>
      Lurer du på hvor firmaet ditt kan levere e-avfall? →
    </button>
  );

  return (
    <div className="sl-eavfall">
      <div className="sl-eavfall__topp">
        <span className="sl-slash">/ nærmeste mottak for ee-avfall</span>
        <button className="sl-eavfall__lukk" onClick={() => setApen(false)} aria-label="Lukk">×</button>
      </div>

      {!steder && (
        <div className="sl-eavfall__sok">
          <button className="sl-btn sl-btn--primary sl-eavfall__pos" onClick={brukPosisjon} disabled={laster}>
            <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.6"
                 strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
              <path d="M12 21s7-6.2 7-11a7 7 0 1 0-14 0c0 4.8 7 11 7 11z"/><circle cx="12" cy="10" r="2.6"/>
            </svg>
            Bruk min posisjon
          </button>
          <span className="sl-eavfall__eller">eller</span>
          <div className="sl-eavfall__felt">
            <input className="sl-felt" value={sted} placeholder="Poststed, postnummer eller kommune"
                   onChange={e => setSted(e.target.value)}
                   onKeyDown={e => { if (e.key === "Enter") { e.preventDefault(); sokSted(); } }}
                   aria-label="Sted"/>
            <button className="sl-btn sl-btn--ghost" onClick={sokSted} disabled={laster}>Søk</button>
          </div>
        </div>
      )}

      {laster && <p className="sl-eavfall__status">Henter mottak i nærheten…</p>}
      {feil && <p className="sl-eavfall__feil" role="alert">{feil}</p>}

      {steder && (
        <>
          <p className="sl-eavfall__fra">Nærmest {fra}:</p>
          <ol className="sl-eavfall__liste">
            {steder.map(s => (
              <li key={s.id}>
                <div className="sl-eavfall__navn">
                  {s.navn}
                  <span className={"sl-eavfall__merke sl-eavfall__merke--" + s.type}>
                    {s.type === "kjede"  ? "kjede · plikter å ta imot"
                     : s.type === "butikk" ? "butikk · plikter å ta imot"
                     : "gjenvinningsstasjon"}
                  </span>
                </div>
                <div className="sl-eavfall__meta">
                  <span className="sl-eavfall__km">{s.km < 1 ? Math.round(s.km * 1000) + " m" : s.km.toFixed(1) + " km"}</span>
                  {s.adresse && <span>{s.adresse}</span>}
                  {s.sted && <span>{s.sted}</span>}
                  {s.drift && <span>{s.drift}</span>}
                </div>
                {s.aapent && <div className="sl-eavfall__tid">{s.aapent}</div>}
                <div className="sl-eavfall__lenker">
                  <a className="sl-eavfall__vei"
                     href={"https://www.google.com/maps/dir/?api=1&destination=" + s.lat + "," + s.lon
                           + "&destination_place_id=&travelmode=driving"}
                     target="_blank" rel="noopener noreferrer">
                    <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.7"
                         strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
                      <path d="M3 11.5 21 4l-7.5 18-2.2-7.3z"/>
                    </svg>
                    Veibeskrivelse
                  </a>
                  <a className="sl-eavfall__kart"
                     href={"https://www.openstreetmap.org/?mlat=" + s.lat + "&mlon=" + s.lon + "#map=17/" + s.lat + "/" + s.lon}
                     target="_blank" rel="noopener noreferrer">kart ↗</a>
                </div>
              </li>
            ))}
          </ol>
          <button className="sl-eavfall__nytt"
                  onClick={() => { setSteder(null); setSted(""); setFeil(""); setLaster(false); }}>
            Søk et annet sted
          </button>
        </>
      )}

      <p className="sl-eavfall__kilde">
        Data fra OpenStreetMap, og kan være ufullstendig. Ring gjerne først.
      </p>
    </div>
  );
}
