/* ===================== Hintalaskuri (price calculator) ===================== */

const eur = (n) => (Math.round(n * 100) / 100).toLocaleString("fi-FI", { minimumFractionDigits: 0, maximumFractionDigits: 2 }) + " €";

// Oulu market square (Rotuaari) as origin for travel-fee calculation
const OULU_LAT = 65.0121, OULU_LON = 25.4681;
const FREE_KM  = 15;   // free travel radius in km
const RATE_KM  = 0.50; // €/km beyond FREE_KM

function haversineKm(lat1, lon1, lat2, lon2) {
  const R = 6371;
  const dLat = (lat2 - lat1) * Math.PI / 180;
  const dLon = (lon2 - lon1) * Math.PI / 180;
  const a = Math.sin(dLat/2)**2 + Math.cos(lat1*Math.PI/180)*Math.cos(lat2*Math.PI/180)*Math.sin(dLon/2)**2;
  return R * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
}

/* window-type glyph */
function WinGlyph({ id }) {
  const s = { width: 56, height: 56, viewBox: "0 0 56 56", fill: "none", stroke: "var(--brand)", strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round" };
  const glass = "color-mix(in oklab, var(--brand) 9%, #fff)";
  switch (id) {
    case "kaide": case "parv_kaide":
      return <svg {...s}><rect x="8" y="14" width="40" height="22" rx="2" fill={glass}/><path d="M8 36h40M16 14v22M28 14v22M40 14v22M6 42h44"/></svg>;
    case "ovi":
      return <svg {...s}><rect x="16" y="6" width="24" height="44" rx="2" fill={glass}/><path d="M28 6v44"/><circle cx="23" cy="30" r="1.4" fill="var(--brand)"/></svg>;
    case "peili":
      return <svg {...s}><rect x="14" y="8" width="28" height="40" rx="14" fill={glass}/><path d="M20 16c0 0 4-2 8 0"/></svg>;
    case "korkea": case "ter_katto":
      return <svg {...s}><path d="M6 20l44-8v10l-44 8z" fill={glass}/><path d="M12 30v18M44 24v18M8 48h40"/></svg>;
    case "ryhma": case "ter_molemmat": case "ter_sisa": case "parv_matala": case "parv_korkea":
      return <svg {...s}><rect x="8" y="10" width="40" height="36" rx="2" fill={glass}/><path d="M28 10v36M8 28h40"/></svg>;
    case "tuuletus":
      return <svg {...s}><rect x="20" y="8" width="16" height="40" rx="2" fill={glass}/><path d="M36 8l6 4v32l-6 4"/></svg>;
    case "kiintea": case "parv_sisa": case "parv_molemmat": case "normaali":
      return <svg {...s}><rect x="12" y="10" width="32" height="36" rx="2" fill={glass}/><path d="M18 16l8 8"/></svg>;
    case "kylpy":
      return <svg {...s}><rect x="8" y="12" width="18" height="32" rx="2" fill={glass}/><rect x="30" y="12" width="18" height="32" rx="2" fill={glass}/></svg>;
    case "av6":
      return <svg {...s}><rect x="14" y="8" width="22" height="40" rx="2" fill={glass}/><path d="M36 8l8 5v30l-8 5M25 8v40"/></svg>;
    case "salekaihdin":
      return <svg {...s}><rect x="10" y="8" width="36" height="40" rx="2" fill={glass}/><path d="M10 18h36M10 26h36M10 34h36M10 42h36"/><path d="M28 8v40" strokeDasharray="3 2" strokeWidth="1.5"/></svg>;
    default: /* av4, ruutu */
      return <svg {...s}><rect x="14" y="8" width="22" height="40" rx="2" fill={glass}/><path d="M36 8l8 5v30l-8 5"/>{id==="ruutu" && <path d="M25 8v40M14 28h22"/>}</svg>;
  }
}

function Stepper({ value, onChange }) {
  return (
    <div style={{ display: "flex", alignItems: "center", gap: 14, justifyContent: "center" }}>
      <button className="step" data-off={value===0} onClick={() => onChange(Math.max(0, value - 1))} aria-label="Vähennä"><Ic.minus/></button>
      <span style={{ fontFamily: "var(--head)", fontWeight: 800, fontSize: 20, minWidth: 28, textAlign: "center", color: value>0 ? "var(--brand)" : "var(--navy)" }}>{value}</span>
      <button className="step step--on" onClick={() => onChange(value + 1)} aria-label="Lisää"><Ic.plus/></button>
    </div>
  );
}

function CalcGroup({ group, qty, setQty, open, toggle, ulko }) {
  return (
    <div className="cgroup">
      <button className="cgroup__head" onClick={toggle}>
        <span className="cgroup__chev" data-open={open}><Ic.chevron/></span>
        {group.label}
        <span className="cgroup__count">{group.items.length} kohdetta</span>
      </button>
      {open && (
        <div className="cgroup__grid">
          {group.items.map((it) => {
            const disabled = !!(ulko && it.noUlko);
            return (
              <div className="witem" key={it.id}
                data-active={!disabled && (qty[it.id]||0) > 0}
                style={disabled ? { opacity: .4, pointerEvents: "none" } : {}}>
                <div className="witem__top">
                  <div className="witem__name">{it.name}</div>
                  <div className="witem__price">{it.price} € <span>/ {it.unit}</span></div>
                </div>
                <div className="witem__glyph"><WinGlyph id={it.id}/></div>
                {disabled
                  ? <div className="witem__hint" style={{color:"var(--muted)"}}>Ei saatavilla ulkopesun yhteydessä</div>
                  : it.hint && <div className="witem__hint">{it.hint}</div>
                }
                {disabled
                  ? <div style={{fontSize:12,color:"var(--faint)",textAlign:"center",padding:"6px 0"}}>—</div>
                  : <Stepper value={qty[it.id] || 0} onChange={(v) => setQty(it.id, v)} />
                }
              </div>
            );
          })}
        </div>
      )}
    </div>
  );
}

function Calculator({ go }) {
  const [qty, setQtyState] = useState(() => {
    try { return JSON.parse(localStorage.getItem("kl_qty") || "{}"); } catch { return {}; }
  });
  const [openGroups, setOpenGroups] = useState(() => Object.fromEntries(D.calcGroups.map(g => [g.id, !!g.open])));
  const [showBooking, setShowBooking] = useState(false);
  const [ulkoRaw, setUlkoRaw] = useState(false);

  // Address + geocoding
  const [addrInput, setAddrInput] = useState("");
  const [geoState, setGeoState] = useState(null); // null | 'loading' | 'ok' | 'fail'
  const [geoKm, setGeoKm] = useState(null);
  const [travelFee, setTravelFee] = useState(0);
  const [suggestions, setSuggestions] = useState([]);
  const debounceRef = useRef(null);
  const addrRef = useRef(null);

  const setQty = (id, v) => setQtyState((q) => {
    const n = { ...q, [id]: v };
    localStorage.setItem("kl_qty", JSON.stringify(n));
    return n;
  });

  // Switching to ulko-only resets noUlko items (e.g. blind cleaning)
  const setUlko = (v) => {
    if (v) {
      setQtyState((q) => {
        const ids = D.calcGroups.flatMap(g => g.items).filter(it => it.noUlko).map(it => it.id);
        const n = { ...q };
        ids.forEach(id => { n[id] = 0; });
        localStorage.setItem("kl_qty", JSON.stringify(n));
        return n;
      });
    }
    setUlkoRaw(v);
  };
  const ulko = ulkoRaw;

  const allItems = useMemo(() => Object.fromEntries(D.calcGroups.flatMap(g => g.items.map(i => [i.id, i]))), []);
  const subtotal = useMemo(() => Object.entries(qty).reduce((s, [id, n]) => s + (allItems[id]?.price || 0) * n, 0), [qty, allItems]);
  const discountedSubtotal = ulko ? Math.round(subtotal * 0.7 * 100) / 100 : subtotal;
  const lines = useMemo(() => Object.entries(qty)
    .filter(([, n]) => n > 0)
    .map(([id, n]) => {
      const it = allItems[id];
      if (!it || (ulko && it.noUlko)) return null;
      const basePrice = it.price || 0;
      const price = ulko ? Math.round(basePrice * 0.7 * 100) / 100 : basePrice;
      return { id, name: it.name, qty: n, unit: it.unit || "kpl", price, total: price * n };
    }).filter(Boolean), [qty, allItems, ulko]);
  const count = Object.values(qty).reduce((s, n) => s + n, 0);
  const grossBase = Math.max(discountedSubtotal, count > 0 ? D.minOrder : 0);
  const gross = grossBase + travelFee;
  const belowMin = count > 0 && discountedSubtotal < D.minOrder;
  const ulkoDiscount = ulko ? subtotal - discountedSubtotal : 0;
  const deduction = grossBase * D.deductionRate;
  const afterDeduction = Math.max(0, grossBase - deduction) + travelFee;

  const applyCoords = (lat, lng) => {
    const km = haversineKm(OULU_LAT, OULU_LON, lat, lng);
    const extraKm = Math.max(0, km - FREE_KM);
    setGeoKm(Math.round(km * 10) / 10);
    setTravelFee(Math.round(extraKm) * RATE_KM);
    setGeoState("ok");
  };

  const getPlaceCoords = async (placeId) => {
    const r = await fetch(`/api/places?placeId=${encodeURIComponent(placeId)}`);
    const d = await r.json();
    const loc = d.result?.geometry?.location;
    if (!loc) throw new Error("no location");
    return loc;
  };

  const geocodeAddress = async (addr) => {
    setGeoState("loading");
    setSuggestions([]);
    try {
      const r = await fetch(`/api/places?q=${encodeURIComponent(addr.trim())}`);
      const data = await r.json();
      const first = data.predictions?.[0];
      if (!first) { setGeoState("fail"); setTravelFee(0); setGeoKm(null); return; }
      const loc = await getPlaceCoords(first.place_id);
      applyCoords(loc.lat, loc.lng);
    } catch {
      setGeoState("fail"); setTravelFee(0); setGeoKm(null);
    }
  };

  useEffect(() => {
    const handler = (e) => {
      if (addrRef.current && !addrRef.current.contains(e.target)) setSuggestions([]);
    };
    document.addEventListener("mousedown", handler);
    return () => document.removeEventListener("mousedown", handler);
  }, []);

  const fetchSuggestions = async (addr) => {
    try {
      const r = await fetch(`/api/places?q=${encodeURIComponent(addr)}`);
      const data = await r.json();
      if (!data.predictions) return;
      setSuggestions(data.predictions.map(p => ({
        short: p.structured_formatting.main_text,
        extra: (p.structured_formatting.secondary_text || "").replace(/, Finland$/, "").replace(/, Suomi$/, ""),
        placeId: p.place_id,
        full: p.description.replace(/, Finland$/, "").replace(/, Suomi$/, ""),
      })));
    } catch {}
  };

  const selectSuggestion = async (s) => {
    setAddrInput(s.full || s.short);
    setSuggestions([]);
    setGeoState("loading");
    try {
      const loc = await getPlaceCoords(s.placeId);
      applyCoords(loc.lat, loc.lng);
    } catch {
      setGeoState("fail"); setTravelFee(0); setGeoKm(null);
    }
  };

  const handleAddrChange = (e) => {
    const v = e.target.value;
    setAddrInput(v);
    setGeoState(null);
    setTravelFee(0);
    setGeoKm(null);
    setSuggestions([]);
    clearTimeout(debounceRef.current);
    if (v.trim().length >= 3) {
      debounceRef.current = setTimeout(() => fetchSuggestions(v), 400);
    }
  };

  const canBook = count > 0 && (geoState === "ok" || geoState === "fail");
  const reset = () => { setQtyState({}); localStorage.removeItem("kl_qty"); };

  return (
    <div className="calc">
      <div className="wrap calc__inner">

        {/* ── summary rail ─────────────────────────────────────── */}
        <aside className="calc__rail">
          <div className="srail">
            <div className="srail__badge"><Ic.euro style={{color:"var(--brand)"}}/> Hintalaskuri</div>

            <div className="srail__total">
              <div className="srail__totalLabel">Hinta yhteensä</div>
              <div className="srail__amount">{eur(gross)}</div>
            </div>

            {belowMin && (
              <div style={{background:"var(--brand-soft)",borderRadius:8,padding:"9px 13px",fontSize:13,color:"var(--navy)",display:"flex",gap:8,alignItems:"center"}}>
                <Ic.spark style={{color:"var(--brand)",width:16,height:16,flex:"none"}}/>
                Minimiveloitus {D.minOrder} € — hinta nostettu automaattisesti.
              </div>
            )}

            <div className="srail__rows">
              {ulko && ulkoDiscount > 0 && (
                <div className="srow srow--disc"><span>Ulkopinnat −30 %</span><b>−{eur(ulkoDiscount)}</b></div>
              )}
              {travelFee > 0 && (
                <div className="srow"><span>Matkustuskulut ({geoKm} km)</span><b>+{eur(travelFee)}</b></div>
              )}
              <div className="srow"><span>Kotitalousvähennys 35 % (2026)</span><b>−{eur(deduction)}</b></div>
              <div className="srow srow--accent">
                <span>Hinta vähennyksen jälkeen <i>arvio</i></span>
                <b>{eur(afterDeduction)}</b>
              </div>
            </div>

            {/* Address → geocoding → travel fee */}
            <div className="srail__zip" style={{marginTop:14}}>
              <label>Kohteen osoite</label>
              <div className="ziprow" style={{position:"relative"}} ref={addrRef}>
                <input
                  placeholder="Katuosoite, kaupunki"
                  value={addrInput}
                  onChange={handleAddrChange}
                  onKeyDown={(e) => {
                    if (e.key === "Enter" && addrInput.trim() && !suggestions.length) geocodeAddress(addrInput);
                    if (e.key === "Escape") setSuggestions([]);
                  }}
                  autoComplete="off"
                />
                <button className="btn btn--ghost"
                  onClick={() => { setSuggestions([]); addrInput.trim() && geocodeAddress(addrInput); }}
                  disabled={!addrInput.trim() || geoState === "loading"}>
                  {geoState === "loading" ? "…" : "Tarkista"}
                </button>
                {suggestions.length > 0 && (
                  <div style={{position:"absolute",top:"100%",left:0,right:0,zIndex:200,background:"#fff",border:"1px solid var(--border,#e2e8f0)",borderRadius:8,boxShadow:"0 4px 20px rgba(0,0,0,.12)",marginTop:4,overflow:"hidden"}}>
                    {suggestions.map((s, i) => (
                      <button key={i} type="button"
                        onMouseDown={(e) => { e.preventDefault(); selectSuggestion(s); }}
                        style={{display:"block",width:"100%",textAlign:"left",padding:"10px 14px",fontSize:13.5,border:"none",borderBottom:i<suggestions.length-1?"1px solid var(--border,#e2e8f0)":"none",background:"transparent",cursor:"pointer",color:"var(--navy)",fontFamily:"var(--body)"}}>
                        <div style={{fontWeight:600,lineHeight:1.3}}>{s.short}</div>
                        {s.extra && <div style={{fontSize:12,color:"var(--muted)",marginTop:2}}>{s.extra}</div>}
                      </button>
                    ))}
                  </div>
                )}
              </div>
              {geoState === "ok" && (
                <div className="zipres zipres--ok">
                  <Ic.check style={{width:14,height:14,flex:"none"}}/>
                  {geoKm} km Oulusta ·{" "}
                  {travelFee > 0 ? `+${eur(travelFee)} matkustuskulua` : "Ei matkustuskuluja"}
                </div>
              )}
              {geoState === "fail" && (
                <div className="zipres zipres--no">
                  Osoitetta ei löydy — tarkista tai tarkennetaan puhelimessa.
                </div>
              )}
            </div>

            {/* Booking button */}
            {canBook
              ? <>
                  <button className="btn btn--primary btn--block btn--lg" onClick={() => setShowBooking(true)}
                    style={{display:"flex",alignItems:"center",justifyContent:"center",gap:9}}>
                    <Ic.cal style={{width:19,height:19}}/> Varaa aika
                  </button>
                  <p style={{fontSize:13,color:"var(--muted)",textAlign:"center",marginTop:4}}>
                    Voit perua ajan milloin tahansa puhelimitse.
                  </p>
                </>
              : <div style={{display:"flex",flexDirection:"column",gap:8,marginTop:14}}>
                  <button className="btn btn--primary btn--block btn--lg" disabled
                    style={{opacity:.42,cursor:"not-allowed"}}>
                    <Ic.cal style={{width:19,height:19}}/>{" "}
                    {count === 0 ? "Valitse ensin ikkunasi" : "Anna osoite ensin"}
                  </button>
                  <p style={{fontSize:12.5,color:"var(--muted)",textAlign:"center"}}>
                    {count === 0
                      ? "Lisää ikkunoita laskuriin — sitten tarkista osoite."
                      : "Syötä kohteen osoite yllä ja paina Tarkista."}
                  </p>
                </div>
            }

            {count > 0 && <button className="srail__reset" onClick={reset}>Tyhjennä valinnat</button>}

            <div className="srail__trust">
              <Ic.shield style={{color:"var(--brand)", flex:"none"}}/>
              <span>Kiinteä hinta · ei piilokuluja · tyytyväisyystakuu</span>
            </div>
            <div className="srail__trust" style={{marginTop:6}}>
              <Ic.wallet style={{color:"var(--muted)", flex:"none"}}/>
              <span>{D.payment.methods.join(" · ")} · {D.payment.business}</span>
            </div>
          </div>
        </aside>

        {/* ── item groups ──────────────────────────────────────── */}
        <div className="calc__main">
          <div className="calc__head">
            <h1>Mitä pestään?</h1>
            <p className="lead">Valitse ikkunat ja määrät — hinta päivittyy automaattisesti.</p>
            <div className="pesutapa-toggle">
              <button
                className={`pesutapa-toggle__btn ${!ulko ? "is-active" : ""}`}
                onClick={() => setUlko(false)}>
                <Ic.window style={{width:16,height:16}}/> Kaikki pinnat
              </button>
              <button
                className={`pesutapa-toggle__btn ${ulko ? "is-active" : ""}`}
                onClick={() => setUlko(true)}>
                <Ic.spark style={{width:16,height:16}}/> Ulkopinnat <span className="pesutapa-toggle__disc">−30 %</span>
              </button>
            </div>
          </div>
          {D.calcGroups.map((g) => (
            <CalcGroup key={g.id} group={g} qty={qty} setQty={setQty} ulko={ulko}
              open={openGroups[g.id]}
              toggle={() => setOpenGroups(o => ({ ...o, [g.id]: !o[g.id] }))} />
          ))}
          <div className="calc__foot">
            <Ic.spark style={{color:"var(--brand)"}}/>
            <div>
              <b>Erikoiskohteita tai iso taloyhtiö?</b>
              <p className="muted" style={{fontSize:14.5}}>Valitse ikkunat laskurista ja soita — sovitaan räätälöity käynti.</p>
            </div>
          </div>
        </div>
      </div>

      <BookingModal
        open={showBooking}
        onClose={() => setShowBooking(false)}
        ctx={{
          qty, lines, count,
          gross, grossBase, travelFee, geoKm,
          deduction, afterDeduction,
          addrInput,
          service: ulko ? "Ulkopinnat" : "Kaikki pinnat",
        }}
      />
    </div>
  );
}

Object.assign(window, { Calculator, eur });
