/* global React, ReactDOM */
// Bike-Konfigurator — App-Root mit Auth, State, Tweaks, Persistence

const { useState, useEffect, useMemo, useCallback, useRef } = React;
const { TweaksPanel, useTweaks, TweakSection, TweakRadio, TweakColor, TweakButton } = window.Tweaks || {};
const { StudioLayout, EditorialLayout, GarageLayout } = window.BikeLayouts;
const { ComponentDrawer, GarageDrawer, UpgradeModal, ExistingBikeModal } = window.BikeUI;
const { CompareView } = window.BikeAdvanced;
const { SLOT_TO_POOL } = window.BikeFmt;
const { AuthScreen } = window.BikeAuth;

// ── Default profile ────────────────────────────────────────────────────────────
const DEFAULT_PROFILE = {
  weight: 92,
  height: 181,
  bike_type: "analog",
  skill_level: "intermediate",
  riding_style: "playful",
  terrain: "trail",
  budget: 3000
};

const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
  "layout": "studio",
  "accent": "#da291c",
  "accentName": "Rosso Corsa"
}/*EDITMODE-END*/;

const ACCENT_PALETTE = [
  { name: "Rosso Corsa", hex: "#da291c" },
  { name: "Hypersail",   hex: "#f6e500" },
  { name: "Mono",        hex: "#ffffff" },
  { name: "Electric",   hex: "#00bcd4" }
];

// ── Auth helpers ───────────────────────────────────────────────────────────────
function getStoredAuth() {
  try {
    const token = localStorage.getItem("bf_token");
    const user  = JSON.parse(localStorage.getItem("bf_user") || "null");
    return token && user ? { token, user } : null;
  } catch { return null; }
}

function authHeaders(token) {
  return { "Authorization": `Bearer ${token}`, "Content-Type": "application/json" };
}

function resizeDataUrl(dataUrl, maxWidth = 720, quality = 0.76) {
  if (!dataUrl || typeof Image === "undefined") return Promise.resolve(dataUrl || null);
  return new Promise(resolve => {
    const img = new Image();
    img.onload = () => {
      const scale = Math.min(1, maxWidth / Math.max(1, img.width));
      const canvas = document.createElement("canvas");
      canvas.width = Math.max(1, Math.round(img.width * scale));
      canvas.height = Math.max(1, Math.round(img.height * scale));
      const ctx = canvas.getContext("2d");
      if (!ctx) { resolve(dataUrl); return; }
      ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
      resolve(canvas.toDataURL("image/jpeg", quality));
    };
    img.onerror = () => resolve(null);
    img.src = dataUrl;
  });
}

async function prepareProfileForSave(profile, overrides, touched, result) {
  const existingBike = profile.existing_bike ? { ...profile.existing_bike } : null;
  if (existingBike?.photo_preview) {
    const resized = await resizeDataUrl(existingBike.photo_preview);
    existingBike.photo_preview = resized && resized.length < 900000 ? resized : null;
    existingBike.photo_saved = Boolean(existingBike.photo_preview);
  }

  return {
    ...profile,
    existing_bike: existingBike,
    saved_state: {
      version: 1,
      overrides: { ...overrides },
      touched_slots: [...touched],
      total_price: result.total_price,
      price_breakdown: result.price_breakdown || null,
      existing_summary: result.existing_summary || null,
      saved_at: Date.now()
    }
  };
}

function buildIdsForSave(result) {
  return Object.fromEntries(Object.entries(result.build).map(([slot, item]) => {
    if (!item) return [slot, null];
    if (slot === "frame" && item.source === "existing_bike") {
      return [slot, item.reference_frame_id || item.id];
    }
    return [slot, item.id];
  }));
}

function overridesFromSavedEntry(entry) {
  const savedOverrides = entry.profile?.saved_state?.overrides;
  if (savedOverrides && typeof savedOverrides === "object") return savedOverrides;
  if (entry.profile?.existing_bike) return {};
  const fallback = {};
  for (const slot in entry.build || {}) {
    if (entry.build[slot] && !slot.startsWith("__")) fallback[slot] = entry.build[slot];
  }
  return fallback;
}

// ── App root ───────────────────────────────────────────────────────────────────
function App({ frames, components }) {
  // Auth
  const [auth, setAuth] = useState(getStoredAuth);
  const [plan, setPlan] = useState("free");
  const [upgradeOpen, setUpgradeOpen] = useState(false);

  const handleAuth = (user, token) => setAuth({ user, token });

  const handleLogout = () => {
    localStorage.removeItem("bf_token");
    localStorage.removeItem("bf_user");
    setAuth(null);
  };

  // Profile
  const [profile, setProfile] = useState(() => {
    try {
      const stored = localStorage.getItem("bf_profile");
      return stored ? JSON.parse(stored) : DEFAULT_PROFILE;
    } catch (e) { return DEFAULT_PROFILE; }
  });

  // Build state
  const [overrides, setOverrides] = useState({});
  const [touched,   setTouched]   = useState(() => new Set(["frame"]));
  const [drawerSlot, setDrawerSlot] = useState(null);
  const [garageOpen,  setGarageOpen]  = useState(false);
  const [compareOpen, setCompareOpen] = useState(false);
  const [existingBikeOpen, setExistingBikeOpen] = useState(false);

  // Saved builds — synced with backend when authenticated
  const [saved, setSaved] = useState([]);

  // Tweaks
  const [tweaks, setTweak] = useTweaks ? useTweaks(TWEAK_DEFAULTS) : [TWEAK_DEFAULTS, () => {}];

  // Persist profile locally
  useEffect(() => {
    localStorage.setItem("bf_profile", JSON.stringify(profile));
  }, [profile]);

  // Apply accent CSS variable
  useEffect(() => {
    const accent = tweaks.accent || "#da291c";
    document.documentElement.style.setProperty("--accent", accent);
    const isLight = ["#f6e500", "#fff200", "#ffffff"].includes(accent.toLowerCase());
    document.documentElement.style.setProperty("--accent-ink", isLight ? "#181818" : "#ffffff");
    const r = parseInt(accent.slice(1,3), 16);
    const g = parseInt(accent.slice(3,5), 16);
    const b = parseInt(accent.slice(5,7), 16);
    document.documentElement.style.setProperty("--accent-soft", `rgba(${r},${g},${b},0.15)`);
  }, [tweaks.accent]);

  // Load plan + saved builds when auth changes
  useEffect(() => {
    if (!auth) { setSaved([]); setPlan("free"); return; }
    fetch("/api/auth/me", { headers: { Authorization: `Bearer ${auth.token}` } })
      .then(r => r.ok ? r.json() : {})
      .then(data => setPlan(data.plan || "free"))
      .catch(() => {});
    fetch("/api/builds", { headers: { Authorization: `Bearer ${auth.token}` } })
      .then(r => r.ok ? r.json() : [])
      .then(data => setSaved(Array.isArray(data) ? data : []))
      .catch(() => setSaved([]));
  }, [auth]);

  // Generate build from profile (via Python API)
  const [baseResult, setBaseResult] = useState(null);
  const [isBuilding, setIsBuilding] = useState(false);

  useEffect(() => {
    if (!frames.length || !components.brakes) return;
    
    let active = true;
    setIsBuilding(true);
    
    fetch(`${window.BIKEFORGE_API_URL}/build`, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify(profile)
    })
    .then(res => res.json())
    .then(data => {
      if (!active) return;
      if (data.detail) {
         console.warn("API Error:", data.detail);
         setBaseResult(window.BikeEngine.generateBuild(profile, frames, components));
      } else {
         const mappedResult = {
            build: data.build,
            scores: data.scores,
            explanation: data.explanation,
            total_price: window.BikeEngine.totalPrice(data.build),
            price_breakdown: window.BikeEngine.calculatePriceBreakdown(data.build, profile),
            warnings: (data.violations || []).map(v => ({ kind: "error", msg: v })).concat(
                      (data.fit_warnings || []).map(w => ({ kind: "info", msg: w }))
            ),
            levels: window.BikeEngine.determineComponentLevels(profile.budget),
            existing_summary: null
         };
         setBaseResult(mappedResult);
      }
    })
    .catch(err => {
      if (!active) return;
      console.warn("API unreachable, falling back to local JS engine.", err);
      setBaseResult(window.BikeEngine.generateBuild(profile, frames, components));
    })
    .finally(() => {
      if (active) setIsBuilding(false);
    });
    
    return () => { active = false; };
  }, [profile, frames, components]);

  // Apply manual overrides on top of the auto build
  const result = useMemo(() => {
    if (!baseResult) return null;
    if (Object.keys(overrides).length === 0) return baseResult;
    const newBuild = { ...baseResult.build };
    for (const slot in overrides) {
      const id   = overrides[slot];
      const pool = slot === "frame" ? frames : components[SLOT_TO_POOL[slot]];
      const found = pool && pool.find(p => p.id === id);
      if (found) {
        const isExistingReplacement = baseResult.existing_summary?.replaced_slots?.includes(slot);
        newBuild[slot] = isExistingReplacement
          ? {
              ...found,
              source: "upgrade_replacement",
              replacement_for_existing: true,
              original_query: profile.existing_bike?.components?.[slot] || "",
              vision_confidence: Math.max(0, Math.min(1, Number(profile.existing_bike?.component_confidence?.[slot] || 0)))
            }
          : found;
      }
    }
    let total = 0;
    Object.values(newBuild).forEach(v => { if (v && v.price) total += v.price; });
    const scores = window.BikeEngine.calculateScores(newBuild, profile);
    const price_breakdown = window.BikeEngine.calculatePriceBreakdown
      ? window.BikeEngine.calculatePriceBreakdown(newBuild, profile)
      : baseResult.price_breakdown;
    return { ...baseResult, build: newBuild, total_price: total, price_breakdown, scores };
  }, [baseResult, overrides, frames, components, profile]);

  const handleSwap = (slot) => {
    setTouched(prev => { const n = new Set(prev); n.add(slot); return n; });
    setDrawerSlot(slot);
  };

  const handlePick = (item) => {
    setOverrides({ ...overrides, [drawerSlot]: item.id });
    setTouched(prev => { const n = new Set(prev); n.add(drawerSlot); return n; });
    setDrawerSlot(null);
  };

  const handleReset = () => {
    setProfile(DEFAULT_PROFILE);
    setOverrides({});
    setTouched(new Set(["frame"]));
  };

  const handleApplyExistingBike = (existingBike) => {
    setProfile(prev => ({
      ...prev,
      terrain: existingBike.terrain || prev.terrain,
      bike_type: existingBike.bike_type || prev.bike_type,
      existing_bike: existingBike
    }));
    const nextTouched = new Set(["frame"]);
    Object.keys(existingBike.components || {}).forEach(slot => nextTouched.add(slot));
    setTouched(nextTouched);
    setOverrides({});
    setExistingBikeOpen(false);
  };

  const handleSave = async () => {
    if (!auth) return;
    const id        = "build_" + Date.now();
    const title     = result.build.frame.name;
    const photoKey  = `image-slot:bike-photo-${result.build.frame.id}`;
    const savedProfile = await prepareProfileForSave(profile, overrides, touched, result);
    let   thumbnail = null;
    try { thumbnail = localStorage.getItem(photoKey); } catch {}
    thumbnail = thumbnail || savedProfile.existing_bike?.photo_preview || null;

    const entry = {
      id,
      title,
      profile: savedProfile,
      build:   buildIdsForSave(result),
      scores:  result.scores,
      total:   result.total_price,
      thumbnail,
      created: Date.now()
    };

    try {
      const res = await fetch("/api/builds", {
        method:  "POST",
        headers: authHeaders(auth.token),
        body:    JSON.stringify(entry)
      });
      if (res.status === 403) {
        const data = await res.json();
        if (data.error === "limit_reached") { setUpgradeOpen(true); return; }
      }
      if (res.ok) {
        setSaved(prev => [{ ...entry }, ...prev]);
        setGarageOpen(true);
      }
    } catch {}
  };

  const handleLoadSaved = (entry) => {
    setProfile(entry.profile);
    setOverrides(overridesFromSavedEntry(entry));
    const savedTouched = entry.profile?.saved_state?.touched_slots;
    if (Array.isArray(savedTouched) && savedTouched.length) {
      setTouched(new Set(savedTouched));
    } else if (entry.profile?.existing_bike) {
      const nextTouched = new Set(["frame"]);
      Object.keys(entry.profile.existing_bike.components || {}).forEach(slot => nextTouched.add(slot));
      setTouched(nextTouched);
    } else {
      setTouched(new Set(Object.keys(entry.build || {}).filter(slot => entry.build[slot])));
    }
    setGarageOpen(false);
  };

  const handleDeleteSaved = async (id) => {
    setSaved(prev => prev.filter(s => s.id !== id));
    if (!auth) return;
    try {
      await fetch(`/api/builds/${id}`, {
        method:  "DELETE",
        headers: { Authorization: `Bearer ${auth.token}` }
      });
    } catch {}
  };

  // ── Auth gate ──────────────────────────────────────────────────────────────
  if (!auth) {
    return <AuthScreen onAuth={handleAuth} />;
  }

  // ── Layout ─────────────────────────────────────────────────────────────────
  if (!baseResult || !result) {
    return (
      <div style={{ display: "flex", height: "100vh", alignItems: "center", justifyContent: "center", background: "var(--surface)", color: "var(--ink)", fontFamily: "system-ui, sans-serif" }}>
        <div style={{ textAlign: "center" }}>
          <div style={{ fontSize: "24px", marginBottom: "16px" }}>⚙️</div>
          <div>Lade BikeForge Engine...</div>
        </div>
      </div>
    );
  }

  const layout = tweaks.layout || "studio";
  const Layout = layout === "editorial" ? EditorialLayout
               : layout === "garage"    ? GarageLayout
               : StudioLayout;

  const layoutProps = {
    profile,
    onProfileChange: (p) => { setProfile(p); setOverrides({}); },
    result,
    frame:      result.build.frame,
    touched,
    onSwap:     handleSwap,
    onSave:     handleSave,
    onReset:    handleReset,
    onOpenGarage: () => setGarageOpen(true),
    onOpenExistingBike: () => setExistingBikeOpen(true),
    onUpgrade:  () => setUpgradeOpen(true),
    savedCount: saved.length,
    plan,
    user:       auth.user,
    onLogout:   handleLogout
  };

  let drawerPool = null;
  if (drawerSlot) {
    drawerPool = drawerSlot === "frame"
      ? frames
      : (components[SLOT_TO_POOL[drawerSlot]] || []);
  }

  return (
    <>
      <Layout {...layoutProps} />

      {drawerSlot && (
        <ComponentDrawer
          slot={drawerSlot}
          currentItem={result.build[drawerSlot]}
          pool={drawerPool}
          frame={result.build.frame}
          profile={profile}
          onPick={handlePick}
          onClose={() => setDrawerSlot(null)}
        />
      )}

      {garageOpen && (
        <GarageDrawer
          saved={saved}
          onClose={() => setGarageOpen(false)}
          onLoad={handleLoadSaved}
          onDelete={handleDeleteSaved}
          onCompare={() => { setGarageOpen(false); setCompareOpen(true); }}
          user={auth.user}
          onLogout={handleLogout}
          plan={plan}
          onUpgrade={() => { setGarageOpen(false); setUpgradeOpen(true); }}
        />
      )}

      {compareOpen && (
        <CompareView
          saved={saved}
          frames={frames}
          components={components}
          onClose={() => setCompareOpen(false)}
        />
      )}

      {existingBikeOpen && (
        <ExistingBikeModal
          profile={profile}
          onApply={handleApplyExistingBike}
          onClose={() => setExistingBikeOpen(false)}
        />
      )}

      {upgradeOpen && (
        <UpgradeModal
          plan={plan}
          token={auth.token}
          onClose={() => setUpgradeOpen(false)}
        />
      )}

      {TweaksPanel && (
        <TweaksPanel title="Tweaks">
          <TweakSection label="Layout" />
          <TweakRadio label="Variante" value={tweaks.layout}
            options={[
              { value: "studio",    label: "Studio"    },
              { value: "editorial", label: "Editorial" },
              { value: "garage",    label: "Garage"    }
            ]}
            onChange={(v) => setTweak("layout", v)} />
          <TweakSection label="Akzent-Farbe" />
          <TweakColor label="Akzent" value={tweaks.accent}
            options={ACCENT_PALETTE.map(p => p.hex)}
            onChange={(v) => setTweak("accent", v)} />
          <TweakSection label="Aktionen" />
          <TweakButton label="Profil zurücksetzen"    onClick={handleReset} />
          <TweakButton label="Overrides verwerfen"    onClick={() => setOverrides({})} secondary />
          <TweakButton label="Abmelden"               onClick={handleLogout} secondary />
        </TweaksPanel>
      )}
    </>
  );
}

// ── Boot ───────────────────────────────────────────────────────────────────────
async function boot() {
  const [framesRes, componentsRes, affiliateRes] = await Promise.all([
    fetch("/data/frames.json"),
    fetch("/data/components.json"),
    fetch("/data/affiliate_partners.json").catch(() => null)
  ]);
  const frames     = await framesRes.json();
  const components = await componentsRes.json();
  window.BikeAffiliatePartners = affiliateRes?.ok ? await affiliateRes.json() : null;
  ReactDOM.createRoot(document.getElementById("root")).render(
    <App frames={frames} components={components} />
  );
}

boot();
