use axum::{
    http::header,
    response::{Html, IntoResponse, Response},
};

pub(crate) fn index_response() -> Response {
    (
        [
            (
                header::CACHE_CONTROL,
                "no-store, no-cache, must-revalidate, max-age=0",
            ),
            (header::PRAGMA, "no-cache"),
            (header::EXPIRES, "0"),
        ],
        Html(INDEX_HTML),
    )
        .into_response()
}

const INDEX_HTML: &str = r##"<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>aster-webui</title>
  <style>
    :root {
      --bg: #0d1117;
      --panel: #121821;
      --line: #243243;
      --text: #edf2f7;
      --muted: #92a3b5;
      --accent: #7ce7bf;
      --accent-2: #d8fd72;
      --danger: #ff7f73;
    }
    * { box-sizing: border-box; }
    body {
      margin: 0;
      background:
        radial-gradient(circle at top left, rgba(124,231,191,.15), transparent 30%),
        radial-gradient(circle at bottom right, rgba(216,253,114,.12), transparent 25%),
        var(--bg);
      color: var(--text);
      font-family: "IBM Plex Sans", sans-serif;
    }
    main { width: min(1320px, calc(100% - 32px)); margin: 0 auto; padding: 24px 0 36px; }
    .hero, .content, .status-grid { display: grid; gap: 18px; }
    .hero { grid-template-columns: 1.4fr 1fr; margin-bottom: 18px; }
    .status-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .card {
      background: linear-gradient(180deg, rgba(16,22,30,.98), rgba(19,25,35,.9));
      border: 1px solid var(--line);
      border-radius: 26px;
      padding: 22px;
    }
    .hero h1 { margin: 0 0 8px; font-size: clamp(2rem, 3vw, 3.4rem); line-height: 1; }
    .hero p, .muted { color: var(--muted); }
    .status strong { display: block; margin-top: 8px; font-size: 1.3rem; }
    .status small { display: block; margin-top: 6px; color: var(--muted); }
    .controls { display: grid; grid-template-columns: 180px 1fr auto; gap: 12px; align-items: end; margin-top: 16px; }
    .toggle-row { display: flex; gap: 10px; flex-wrap: wrap; margin-top: 12px; }
    .toggle-pill {
      display: inline-flex; align-items: center; gap: 10px; padding: 10px 14px;
      border: 1px solid var(--line); border-radius: 999px; background: rgba(9,14,20,.7);
      color: var(--text); font-size: .95rem;
    }
    .toggle-pill input { margin: 0; width: 18px; height: 18px; accent-color: var(--accent); }
    label { display: grid; gap: 8px; color: var(--muted); font-size: .9rem; }
    input, button {
      border-radius: 14px;
      border: 1px solid var(--line);
      padding: 12px 14px;
      font: inherit;
    }
    input { background: rgba(6,10,15,.8); color: var(--text); }
    .actions { display: flex; gap: 10px; flex-wrap: wrap; }
    button { cursor: pointer; }
    .primary { background: linear-gradient(135deg, var(--accent), var(--accent-2)); color: #071016; font-weight: 700; }
    .ghost { background: rgba(9,14,20,.7); color: var(--text); }
    .danger { background: rgba(255,127,115,.14); color: #ffc7c1; }
    .content { grid-template-columns: 1.45fr 1fr; }
    .metrics-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 18px; margin-bottom: 18px; }
    .gallery { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 12px; }
    .tile {
      border: 1px solid var(--line);
      border-radius: 20px;
      overflow: hidden;
      background: rgba(8,12,18,.78);
    }
    .tile.selected { outline: 2px solid var(--accent-2); }
    .tile img { width: 100%; aspect-ratio: 960 / 376; object-fit: cover; display: block; }
    .tile-body { padding: 12px; display: grid; gap: 10px; }
    .tile-title { word-break: break-word; }
    .mini { display: flex; gap: 8px; flex-wrap: wrap; }
    .order { display: grid; gap: 10px; }
    .order-item {
      display: grid; grid-template-columns: auto 1fr auto; gap: 12px; align-items: center;
      border: 1px solid var(--line); border-radius: 18px; padding: 12px 14px; background: rgba(8,12,18,.78);
    }
    .index { color: var(--accent-2); font-weight: 700; }
    .meta { display: grid; gap: 4px; }
    .toast {
      position: fixed; bottom: 16px; right: 16px; min-width: 260px; max-width: 420px;
      padding: 14px 16px; border-radius: 14px; border: 1px solid var(--line);
      background: rgba(10,14,20,.94); display: none;
    }
    @media (max-width: 1024px) {
      .hero, .content, .controls, .status-grid, .metrics-grid { grid-template-columns: 1fr; }
    }
    .nyan-wrap {
      position: fixed;
      right: 24px;
      bottom: 18px;
      width: 260px;
      pointer-events: none;
      z-index: 30;
      opacity: 0.98;
      filter: drop-shadow(0 12px 24px rgba(0, 0, 0, 0.35));
    }
    .nyan-wrap img {
      display: block;
      width: 100%;
      height: auto;
    }

    @media (max-width: 900px) {
      .nyan-wrap {
        right: 14px;
        bottom: 12px;
        width: 180px;
      }
    }
</style>
</head>
<body>
  <main>
    <section class="hero">
      <div class="card">
        <p class="muted">aoostar-rs fork</p>
        <h1>aster-webui</h1>
        <p>Eigene Rust-WebUI mit nativer Display-Logik. Bilder werden auf 960x376 normalisiert, GIFs als animierte Frame-Sets importiert; die Rotation laeuft direkt ueber `aoostar-rs` ohne Vendor-Binary.</p>
        <div class="controls">
          <label>
            <span>Switch Time</span>
            <input id="switchTime" type="number" min="1" max="600" value="10">
          </label>
          <label>
            <span>Upload New Source</span>
            <input id="upload" type="file" accept="image/*">
          </label>
          <div class="actions">
            <button id="apply" class="primary" type="button">Apply Rotation</button>
            <button id="disable" class="danger" type="button">Disable</button>
            <button id="refresh" class="ghost" type="button">Refresh</button>
          </div>
        </div>
        <div class="toggle-row">
          <label class="toggle-pill"><input id="specsEnabled" type="checkbox"> System Specs on LCD</label>
          <label class="toggle-pill"><input id="memesEnabled" type="checkbox"> Memes on LCD</label>
          <label class="toggle-pill"><input id="gifsEnabled" type="checkbox"> GIFs on LCD</label>
        </div>
      </div>
      <div class="status-grid">
        <div class="card status"><span class="muted">Custom Panel</span><strong id="customPanelValue">-</strong></div>
        <div class="card status"><span class="muted">Active Images</span><strong id="imageCountValue">-</strong></div>
        <div class="card status"><span class="muted">Display Status</span><strong id="displayStatusValue">-</strong></div>
        <div class="card status"><span class="muted">Current Frame</span><strong id="currentImageValue" style="font-size: 1rem;">-</strong></div>
        <div class="card status"><span class="muted">Display Target</span><strong id="displayTargetValue" style="font-size: 1rem;">-</strong></div>
        <div class="card status"><span class="muted">Config Path</span><strong id="configPathValue" style="font-size: 1rem;">-</strong></div>
      </div>
    </section>
    <section class="metrics-grid">
      <div class="card status"><span class="muted">CPU</span><strong id="cpuUsageValue">-</strong><small id="cpuMetaValue">-</small></div>
      <div class="card status"><span class="muted">Memory</span><strong id="memUsageValue">-</strong><small id="memMetaValue">-</small></div>
      <div class="card status"><span class="muted">Storage</span><strong id="diskUsageValue">-</strong><small id="diskMetaValue">-</small></div>
      <div class="card status"><span class="muted">Swap</span><strong id="swapUsageValue">-</strong><small id="swapMetaValue">-</small></div>
      <div class="card status"><span class="muted">Load Avg</span><strong id="loadValue">-</strong><small id="uptimeValue">-</small></div>
      <div class="card status"><span class="muted">Temperatures</span><strong id="tempCpuValue">-</strong><small id="tempGpuValue">-</small></div>
    </section>
    <section class="content">
      <div class="card">
        <h2>Available Images</h2>
        <div id="gallery" class="gallery"></div>
      </div>
      <div class="card">
        <h2>Rotation Order</h2>
        <div id="order" class="order"></div>
        <p id="displayErrorValue" class="muted"></p>
      </div>
    </section>
  </main>
  <div id="toast" class="toast"></div>
  <div class="nyan-wrap" aria-hidden="true">
    <img src="/api/assets/nyan-cat.gif" alt="Nyancat">
  </div>

  <script>
    const state = {
      images: [],
      activeImages: [],
      setup: { switch_time: "10", custom_panel: false, specs_enabled: false, memes_enabled: false, gifs_enabled: true },
      display: {},
      system: {},
      dirty: false,
    };
    const el = {
      gallery: document.getElementById("gallery"),
      order: document.getElementById("order"),
      switchTime: document.getElementById("switchTime"),
      specsEnabled: document.getElementById("specsEnabled"),
      memesEnabled: document.getElementById("memesEnabled"),
      gifsEnabled: document.getElementById("gifsEnabled"),
      customPanelValue: document.getElementById("customPanelValue"),
      imageCountValue: document.getElementById("imageCountValue"),
      displayStatusValue: document.getElementById("displayStatusValue"),
      currentImageValue: document.getElementById("currentImageValue"),
      displayTargetValue: document.getElementById("displayTargetValue"),
      displayErrorValue: document.getElementById("displayErrorValue"),
      configPathValue: document.getElementById("configPathValue"),
      cpuUsageValue: document.getElementById("cpuUsageValue"),
      cpuMetaValue: document.getElementById("cpuMetaValue"),
      memUsageValue: document.getElementById("memUsageValue"),
      memMetaValue: document.getElementById("memMetaValue"),
      diskUsageValue: document.getElementById("diskUsageValue"),
      diskMetaValue: document.getElementById("diskMetaValue"),
      swapUsageValue: document.getElementById("swapUsageValue"),
      swapMetaValue: document.getElementById("swapMetaValue"),
      loadValue: document.getElementById("loadValue"),
      uptimeValue: document.getElementById("uptimeValue"),
      tempCpuValue: document.getElementById("tempCpuValue"),
      tempGpuValue: document.getElementById("tempGpuValue"),
      upload: document.getElementById("upload"),
      apply: document.getElementById("apply"),
      disable: document.getElementById("disable"),
      refresh: document.getElementById("refresh"),
      toast: document.getElementById("toast"),
    };

    function toast(msg) {
      el.toast.textContent = msg;
      el.toast.style.display = "block";
      clearTimeout(toast.timer);
      toast.timer = setTimeout(() => el.toast.style.display = "none", 2600);
    }

    function bytes(n) {
      if (n < 1024) return n + " B";
      if (n < 1024 * 1024) return (n / 1024).toFixed(1) + " KB";
      return (n / 1024 / 1024).toFixed(1) + " MB";
    }

    function move(index, delta) {
      const target = index + delta;
      if (target < 0 || target >= state.activeImages.length) return;
      const next = [...state.activeImages];
      const [item] = next.splice(index, 1);
      next.splice(target, 0, item);
      state.activeImages = next;
      markDirty();
      render();
    }

    function toggle(name) {
      if (state.activeImages.includes(name)) {
        state.activeImages = state.activeImages.filter((item) => item !== name);
      } else {
        state.activeImages = [...state.activeImages, name];
      }
      markDirty();
      render();
    }

    function markDirty() {
      state.dirty = true;
      updateDirtyUi();
    }

    function clearDirty() {
      state.dirty = false;
      updateDirtyUi();
    }

    function updateDirtyUi() {
      el.apply.textContent = state.dirty ? "Apply Rotation *" : "Apply Rotation";
      el.imageCountValue.textContent = `${state.activeImages.length}${state.dirty ? " *" : ""}`;
    }

    async function request(url, options = {}) {
      const res = await fetch(url, options);
      const data = await res.json().catch(() => ({}));
      if (!res.ok) throw new Error(data.error || "request failed");
      return data;
    }

    async function load(options = {}) {
      const force = !!options.force;
      const preserveDraft = !!options.preserveDraft;
      if (state.dirty && !force) return;

      const draftActiveImages = [...state.activeImages];
      const data = await request("/api/state");
      state.images = data.images;
      state.activeImages = preserveDraft
        ? draftActiveImages.filter((name) => data.images.some((image) => image.name === name))
        : data.active_images;
      state.setup = preserveDraft
        ? {
            ...data.setup,
            switch_time: el.switchTime.value || data.setup.switch_time || "10",
            specs_enabled: el.specsEnabled.checked,
            memes_enabled: el.memesEnabled.checked,
            gifs_enabled: el.gifsEnabled.checked,
          }
        : data.setup;
      state.display = data.display || {};
      state.system = data.system || {};
      if (!preserveDraft) {
        el.switchTime.value = data.setup.switch_time || "10";
        el.specsEnabled.checked = !!data.setup.specs_enabled;
        el.memesEnabled.checked = !!data.setup.memes_enabled;
        el.gifsEnabled.checked = data.setup.gifs_enabled !== false;
      }
      const enabledModes = [
        state.setup.specs_enabled ? "Specs" : "",
        state.setup.memes_enabled ? "Memes" : "",
        state.setup.gifs_enabled ? "GIFs" : "",
      ].filter(Boolean);
      el.customPanelValue.textContent = state.setup.custom_panel
        ? (enabledModes.join(" + ") || "Active")
        : "Disabled";
      el.displayStatusValue.textContent = state.display.connected
        ? `Connected (${state.display.mode || "native"})`
        : (state.display.native_enabled ? "Disconnected" : "Disabled");
      el.currentImageValue.textContent = state.display.current_image || "-";
      el.displayTargetValue.textContent = state.display.device || "-";
      el.displayErrorValue.textContent = state.display.last_error || "";
      el.configPathValue.textContent = data.monitor_path;
      el.cpuUsageValue.textContent = `${state.system.cpu_usage_percent || "-"} %`;
      el.cpuMetaValue.textContent = `Load ${state.system.load_avg_one || "-"} / ${state.system.load_avg_five || "-"} / ${state.system.load_avg_fifteen || "-"}`;
      el.memUsageValue.textContent = `${state.system.mem_usage_percent || "-"} %`;
      el.memMetaValue.textContent = `${state.system.mem_used || "-"} / ${state.system.mem_total || "-"}`;
      el.diskUsageValue.innerHTML = `/mnt/cache ${state.system.cache_usage_percent || "-"}%<br>/mnt/user ${state.system.user_usage_percent || "-"}%`;
      el.diskMetaValue.textContent = `${state.system.cache_used || "-"} / ${state.system.cache_total || "-"} | ${state.system.user_used || "-"} / ${state.system.user_total || "-"}`;
      el.swapUsageValue.textContent = `${state.system.swap_usage_percent || "-"} %`;
      el.swapMetaValue.textContent = `${state.system.swap_used || "-"} / ${state.system.swap_total || "-"}`;
      el.loadValue.textContent = `${state.system.cpu_count || "-"} CPU / ${state.system.process_count || "-"} proc`;
      el.uptimeValue.textContent = `Uptime ${state.system.uptime || "-"}`;
      el.tempCpuValue.textContent = `CPU ${state.system.temperature_cpu || "-"}`;
      el.tempGpuValue.textContent = `GPU ${state.system.temperature_gpu || "-"}`;
      updateDirtyUi();
      render();
    }

    async function uploadFile(event) {
      const file = event.target.files[0];
      if (!file) return;
      const form = new FormData();
      form.append("file", file);
      await request("/api/upload", { method: "POST", body: form });
      event.target.value = "";
      toast("Upload complete");
      await load({ force: true, preserveDraft: state.dirty });
    }

    async function apply() {
      await request("/api/panels/activate", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({
          images: state.activeImages,
          switch_time: Number.parseInt(el.switchTime.value || "10", 10),
          specs_enabled: el.specsEnabled.checked,
          memes_enabled: el.memesEnabled.checked,
          gifs_enabled: el.gifsEnabled.checked,
        }),
      });
      clearDirty();
      toast("Rotation updated");
      await load({ force: true });
    }

    async function disablePanels() {
      await request("/api/panels/disable", { method: "POST" });
      clearDirty();
      toast("Custom panels disabled");
      await load({ force: true });
    }

    async function removeImage(name) {
      await request("/api/images/delete", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ name }),
      });
      state.activeImages = state.activeImages.filter((item) => item !== name);
      clearDirty();
      toast("Image deleted");
      await load({ force: true });
    }

    function render() {
      const imageMap = new Map(state.images.map((image) => [image.name, image]));
      el.gallery.innerHTML = "";
      for (const image of state.images) {
        const selected = state.activeImages.includes(image.name);
        const tile = document.createElement("article");
        tile.className = "tile" + (selected ? " selected" : "");
        tile.innerHTML = `
          <img src="${image.url}" alt="${image.name}">
          <div class="tile-body">
            <div class="tile-title">${image.label}</div>
            <div class="muted">${bytes(image.size)}${image.animated ? ` • GIF • ${image.frame_count} Frames` : ""}</div>
            <div class="mini">
              <button class="ghost" type="button">${selected ? "Remove" : "Add"}</button>
              <button class="danger" type="button">Delete</button>
            </div>
          </div>
        `;
        const [toggleBtn, deleteBtn] = tile.querySelectorAll("button");
        toggleBtn.addEventListener("click", () => toggle(image.name));
        deleteBtn.addEventListener("click", () => removeImage(image.name));
        el.gallery.appendChild(tile);
      }

      el.order.innerHTML = "";
      if (state.setup.specs_enabled && !state.activeImages.length) {
        const row = document.createElement("div");
        row.className = "order-item";
        row.innerHTML = `
          <div class="index">S</div>
          <div class="meta">
            <div>System Specs</div>
            <div class="muted">${state.display.current_image === "System Specs" ? "Currently shown" : "Fixed first frame when enabled"}</div>
          </div>
          <div class="mini"><span class="muted">Toggle above</span></div>
        `;
        el.order.appendChild(row);
      }

      if (state.setup.specs_enabled && state.activeImages.length) {
        const row = document.createElement("div");
        row.className = "order-item";
        row.innerHTML = `
          <div class="index">S</div>
          <div class="meta">
            <div>System Specs Overlay</div>
            <div class="muted">Live stats are drawn over every meme while enabled.</div>
          </div>
          <div class="mini"><span class="muted">Toggle above</span></div>
        `;
        el.order.appendChild(row);
      }

      if (!state.activeImages.length && !state.setup.specs_enabled) {
        el.order.innerHTML = `<p class="muted">No active images.</p>`;
        return;
      }
      state.activeImages.forEach((name, index) => {
        const image = imageMap.get(name);
        const label = image ? image.label : name;
        const row = document.createElement("div");
        row.className = "order-item";
        const isCurrent = state.display.current_image === name || state.display.current_image === label;
        row.innerHTML = `
          <div class="index">${index + 1}</div>
          <div class="meta">
            <div>${label}</div>
            <div class="muted">${isCurrent ? "Currently shown" : (image && image.animated ? `Animated • ${image.frame_count} Frames` : "")}</div>
          </div>
          <div class="mini">
            <button class="ghost" type="button">Up</button>
            <button class="ghost" type="button">Down</button>
            <button class="danger" type="button">Remove</button>
          </div>
        `;
        const [up, down, remove] = row.querySelectorAll("button");
        up.addEventListener("click", () => move(index, -1));
        down.addEventListener("click", () => move(index, 1));
        remove.addEventListener("click", () => toggle(name));
        el.order.appendChild(row);
      });
    }

    el.upload.addEventListener("change", (event) => uploadFile(event).catch((err) => toast(err.message)));
    el.apply.addEventListener("click", () => apply().catch((err) => toast(err.message)));
    el.disable.addEventListener("click", () => disablePanels().catch((err) => toast(err.message)));
    el.refresh.addEventListener("click", () => {
      clearDirty();
      load({ force: true }).catch((err) => toast(err.message));
    });
    el.switchTime.addEventListener("change", markDirty);
    el.specsEnabled.addEventListener("change", markDirty);
    el.memesEnabled.addEventListener("change", markDirty);
    el.gifsEnabled.addEventListener("change", markDirty);

    load({ force: true }).catch((err) => toast(err.message));
    setInterval(() => load().catch(() => {}), 5000);
  </script>
</body>
</html>
"##;
