import React, { useMemo, useRef, useState } from "react";
import { convertFileSrc } from "@tauri-apps/api/core";
import { basename, join } from "@tauri-apps/api/path";
import { open } from "@tauri-apps/plugin-dialog";
import { openUrl } from "@tauri-apps/plugin-opener";
import { copyFile, exists, readTextFile, writeTextFile } from "@tauri-apps/plugin-fs";
import { parseLuaItems, buildLuaFromItems } from "./parser/luaParser.js";
import pkg from "../package.json";
import appConfig from "./config/appConfig.js";

const categoryIcons = {
  All: "/icons/all.png",
  "Nicht zugeordnet": "/icons/unassigned.png",
  Weapons: "/icons/weapons.png",
  Ammo: "/icons/ammo.png",
  Attachments: "/icons/attachments.png",
  Consumables: "/icons/consumables.png",
  "Non-Consumables": "/icons/items.png",
};

const APP_VERSION = appConfig?.version || pkg?.version || "0.6.5";
const APP_VERSION_LABEL = appConfig?.versionLabelSuffix
  ? `${APP_VERSION} ${appConfig.versionLabelSuffix}`
  : APP_VERSION;
const rareOptions = ["common", "epic", "legendary"];
const ammoOptions = [
  "nil",
  "AMMO_PISTOL",
  "AMMO_REVOLVER",
  "AMMO_RIFLE",
  "AMMO_REPEATER",
  "AMMO_SHOTGUN",
  "AMMO_SNIPER",
  "AMMO_ARROW",
  "AMMO_THROWING_KNIVES",
  "AMMO_DYNAMITE",
];

const ALL_CATEGORY = "__ALL__";
const UNCATEGORIZED = "Nicht zugeordnet";
const translations = {
  de: {
    allItems: "Alle Items",
    uncategorized: "Nicht zugeordnet",
    categories: "Kategorien",
    category: "Kategorie",
    newCategoryPlaceholder: "Neue Kategorie anlegen...",
    searchPlaceholder: "Item suchen...",
    inventoryOpen: "Inventory \u00f6ffnen",
    inventoryLabel: "Inventory",
    loadedFile: "Geladen",
    chooseItemHint: "W\u00e4hle links eine Kategorie und dann ein Item aus.",
    applyChanges: "\u00c4nderungen \u00fcbernehmen",
    selectInventoryTitle: "QS Inventory Ordner ausw\u00e4hlen",
    itemsFileMissing: "items.lua nicht gefunden. Bitte den qs-inventory Ordner w\u00e4hlen.",
    imagesMissing: "Bilderordner nicht gefunden: qs-inventory\\html\\images",
    noItemsDetected:
      "Keine Items erkannt. Stelle sicher, dass die Datei mit `ItemList = {` oder `QBShared.Items = {` beginnt.",
    loadedItems: ({ count }) => `Geladen: ${count} Items`,
    loadError: ({ message }) => `Fehler beim Laden: ${message}`,
    saved: "Gespeichert",
    saveError: ({ message }) => `Fehler beim Speichern: ${message}`,
    backupError: ({ message }) => `Backup konnte nicht erstellt werden: ${message}`,
    language: "Sprache",
    newItem: "Neues Item",
    template: "Template",
    create: "Erstellen",
    cancel: "Abbrechen",
    itemId: "Item ID",
    itemLabel: "Label",
    idRequired: "Bitte eine Item-ID angeben.",
    idExists: ({ id }) => `Item-ID existiert bereits: ${id}`,
    templateNormal: "Normale Items",
    templateNormalRare: "Normale Items mit Rarit\u00e4t",
    templateWeapon: "Waffen",
    templateFood: "Nahrungsmittel",
    templateDrink: "Getränk",
    selectTemplateTitle: "Template auswählen",
    createItemTitle: "Neues Item bearbeiten",
    close: "Schließen",
    itemCreated: "Item gespeichert",
    imageAdd: "Bild hinzufügen",
    imageAddError: ({ message }) => `Bild konnte nicht hinzugefügt werden: ${message}`,
    imageAdded: "Bild hinzugefügt",
  },
  en: {
    allItems: "All Items",
    uncategorized: "Unassigned",
    categories: "Categories",
    category: "Category",
    newCategoryPlaceholder: "Create new category...",
    searchPlaceholder: "Search items...",
    inventoryOpen: "Open inventory",
    inventoryLabel: "Inventory",
    loadedFile: "Loaded",
    chooseItemHint: "Choose a category on the left and then select an item.",
    applyChanges: "Apply changes",
    selectInventoryTitle: "Select QS inventory folder",
    itemsFileMissing: "items.lua not found. Please select the qs-inventory folder.",
    imagesMissing: "Images folder not found: qs-inventory\\html\\images",
    noItemsDetected:
      "No items detected. Ensure the file starts with `ItemList = {` or `QBShared.Items = {`.",
    loadedItems: ({ count }) => `Loaded: ${count} items`,
    loadError: ({ message }) => `Load error: ${message}`,
    saved: "Saved",
    saveError: ({ message }) => `Save error: ${message}`,
    backupError: ({ message }) => `Backup failed: ${message}`,
    language: "Language",
    newItem: "New Item",
    template: "Template",
    create: "Create",
    cancel: "Cancel",
    itemId: "Item ID",
    itemLabel: "Label",
    idRequired: "Please provide an item ID.",
    idExists: ({ id }) => `Item ID already exists: ${id}`,
    templateNormal: "Normal Items",
    templateNormalRare: "Normal Items with Rarity",
    templateWeapon: "Weapons",
    templateFood: "Food",
    templateDrink: "Drink",
    selectTemplateTitle: "Select template",
    createItemTitle: "Edit new item",
    close: "Close",
    itemCreated: "Item saved",
    imageAdd: "Add image",
    imageAddError: ({ message }) => `Failed to add image: ${message}`,
    imageAdded: "Image added",
  },
};

const itemTemplates = [
  {
    key: "normal",
    labelKey: "templateNormal",
    build: ({ id, label, category }) => ({
      id,
      category,
      name: id,
      label: label || id,
      weight: 0,
      type: "item",
      image: "",
      unique: false,
      useable: true,
      shouldClose: true,
      combinable: null,
      description: "",
    }),
  },
  {
    key: "normal_rare",
    labelKey: "templateNormalRare",
    build: ({ id, label, category }) => ({
      id,
      category,
      name: id,
      label: label || id,
      weight: 0,
      type: "item",
      image: "",
      unique: false,
      useable: true,
      shouldClose: true,
      combinable: null,
      description: "",
      rare: "common",
    }),
  },
  {
    key: "weapon",
    labelKey: "templateWeapon",
    build: ({ id, label, category }) => ({
      id,
      category,
      name: id,
      label: label || id,
      weight: 1000,
      type: "weapon",
      ammotype: null,
      image: "",
      unique: true,
      useable: false,
      description: "",
    }),
  },
  {
    key: "food",
    labelKey: "templateFood",
    build: ({ id, label, category }) => ({
      id,
      category,
      name: id,
      label: label || id,
      weight: 0,
      type: "item",
      image: "",
      unique: false,
      useable: false,
      shouldClose: false,
      combinable: null,
      description: "",
      created: null,
      decay: 0,
      delete: false,
      object: "",
      useableIn: "",
      client: {
        status: { hunger: 0 },
        usetime: 0,
        anim: { dict: "", clip: "" },
        prop: {
          model: "",
          pos: "",
          rot: "",
          bone: 0,
        },
        disable: { move: false, car: false, mouse: false, combat: false },
        removeAfterUse: false,
      },
    }),
  },
  {
    key: "drink",
    labelKey: "templateDrink",
    build: ({ id, label, category }) => ({
      id,
      category,
      name: id,
      label: label || id,
      weight: 0,
      type: "item",
      image: "",
      unique: false,
      useable: false,
      shouldClose: false,
      combinable: null,
      created: null,
      decay: 0,
      delete: false,
      object: "",
      description: "",
      rare: "common",
      client: {
        status: { thirst: 0 },
        usetime: 0,
        anim: { dict: "", clip: "" },
        prop: {
          model: "",
          bone: 0,
          pos: "",
          rot: "",
        },
        disable: { move: false, car: false, mouse: false, combat: false },
        removeAfterUse: false,
      },
    }),
  },
];

function isPlainObject(v) {
  return v && typeof v === "object" && !Array.isArray(v);
}

function parseVec3String(value) {
  if (typeof value !== "string") return null;
  const match = value.match(/^(vec3|vector3)\s*\(\s*([-\d.]+)\s*,\s*([-\d.]+)\s*,\s*([-\d.]+)\s*\)\s*$/i);
  if (!match) return null;
  return {
    ctor: match[1],
    x: Number(match[2]),
    y: Number(match[3]),
    z: Number(match[4]),
  };
}

export default function App() {
  // dark default
  document.documentElement.classList.add("dark");

  const backupDoneRef = useRef(new Set());
  const [items, setItems] = useState([]);
  const [filePath, setFilePath] = useState(null);
  const [loadedFile, setLoadedFile] = useState(null);
  const [inventoryRoot, setInventoryRoot] = useState(null);
  const [imagesDir, setImagesDir] = useState(null);
  const [categoriesPath, setCategoriesPath] = useState(null);

  const [language, setLanguage] = useState("de");
  const [selectedCategory, setSelectedCategory] = useState(ALL_CATEGORY);
  const [selectedItemId, setSelectedItemId] = useState(null);
  const [search, setSearch] = useState("");
  const [status, setStatus] = useState("");
  const [newItemStage, setNewItemStage] = useState(null);
  const [newItemDraft, setNewItemDraft] = useState(null);
  const [newItemTemplate, setNewItemTemplate] = useState(itemTemplates[0]?.key || "basic");
  const [toastMessage, setToastMessage] = useState("");
  const toastTimerRef = useRef(null);

  const t = (key, vars) => {
    const entry = translations[language]?.[key] ?? translations.en[key] ?? key;
    return typeof entry === "function" ? entry(vars || {}) : entry;
  };

  function showToast(message) {
    if (toastTimerRef.current) {
      clearTimeout(toastTimerRef.current);
    }
    setToastMessage(message);
    toastTimerRef.current = setTimeout(() => {
      setToastMessage("");
    }, 1400);
  }

  const displayCategoryName = (cat) => {
    if (cat === ALL_CATEGORY) return t("allItems");
    if (cat === UNCATEGORIZED) return t("uncategorized");
    return cat;
  };

  const categories = useMemo(() => {
    const set = new Set(items.map((i) => i.category || UNCATEGORIZED));
    return [ALL_CATEGORY, ...Array.from(set)];
  }, [items]);

  const categoryCounts = useMemo(() => {
    const counts = {};
    for (const item of items) {
      const cat = item.category || UNCATEGORIZED;
      counts[cat] = (counts[cat] || 0) + 1;
    }
    counts[ALL_CATEGORY] = items.length;
    return counts;
  }, [items]);

  const categoryOptions = useMemo(
    () => categories.filter((c) => c !== ALL_CATEGORY),
    [categories]
  );

  const filteredItems = useMemo(() => {
    let list = items;

    if (selectedCategory !== ALL_CATEGORY) {
      list = list.filter((i) => (i.category || UNCATEGORIZED) === selectedCategory);
    }

    const q = search.trim().toLowerCase();
    if (q) {
      list = list.filter((i) => {
        const label = (i.label || "").toLowerCase();
        const id = (i.id || "").toLowerCase();
        return label.includes(q) || id.includes(q);
      });
    }

    return list;
  }, [items, selectedCategory, search]);

  const selectedItem = useMemo(() => {
    return items.find((i) => i.id === selectedItemId) || null;
  }, [items, selectedItemId]);

  const selectedImageSrc = selectedItem ? getImageSrc(selectedItem) : null;

  function buildImagePath(dir, name) {
    if (!dir || !name) return null;
    const trimmedDir = dir.replace(/[\\/]+$/, "");
    const trimmedName = String(name).replace(/^[/\\]+/, "");
    const sep = trimmedDir.includes("\\") ? "\\" : "/";
    return `${trimmedDir}${sep}${trimmedName}`;
  }

  function getImageName(item) {
    return item?.image || item?.img || item?.icon || null;
  }

  function getImageSrc(item) {
    const imageName = getImageName(item);
    const imagePath = buildImagePath(imagesDir, imageName);
    return imagePath ? convertFileSrc(imagePath) : null;
  }

  function makeTimestamp() {
    const d = new Date();
    const pad = (v) => String(v).padStart(2, "0");
    return `${pad(d.getDate())}${pad(d.getMonth() + 1)}${d.getFullYear()}-${pad(
      d.getHours()
    )}${pad(d.getMinutes())}`;
  }

  async function ensureBackup(itemsPath, text) {
    if (!itemsPath || backupDoneRef.current.has(itemsPath)) return;
    try {
      const backupPath = `${itemsPath}-${makeTimestamp()}.bak`;
      await writeTextFile(backupPath, text);
      backupDoneRef.current.add(itemsPath);
    } catch (e) {
      setStatus(t("backupError", { message: e?.message || String(e) }));
    }
  }

  function buildCategoryMap(itemsList) {
    const map = {};
    for (const item of itemsList) {
      map[item.id] = item.category || UNCATEGORIZED;
    }
    return map;
  }

  function applyCategoryMap(itemsList, categoryMap) {
    if (!categoryMap || typeof categoryMap !== "object") return itemsList;
    return itemsList.map((item) => ({
      ...item,
      category: categoryMap[item.id] || item.category || UNCATEGORIZED,
    }));
  }

  async function handleOpenInventory() {
    setStatus("");
    const selected = await open({
      multiple: false,
      directory: true,
      title: t("selectInventoryTitle"),
    });
    if (!selected) return;

    try {
      let base = selected.replace(/[\\/]+$/, "");
      const lastSegment = base.split(/[\\/]/).pop()?.toLowerCase();

      if (lastSegment !== "qs-inventory") {
        const candidate = await join(base, "qs-inventory");
        if (await exists(candidate)) {
          base = candidate;
        }
      }

      const itemsPath = await join(base, "shared", "items.lua");
      if (!(await exists(itemsPath))) {
        setStatus(t("itemsFileMissing"));
        return;
      }

      const imagesPath = await join(base, "html", "images");
      if (!(await exists(imagesPath))) {
        setStatus(t("imagesMissing"));
        return;
      }

      const categoriesFile = await join(base, "shared", "items.categories.json");
      const text = await readTextFile(itemsPath);
      await ensureBackup(itemsPath, text);
      const parsed = parseLuaItems(text);
      const hasRootTable = /(ItemList|QBShared\.Items)\s*=\s*{[\s\S]*}\s*$/m.test(text);
      const isEmptyFile = text.replace(/--.*$/gm, "").trim() === "";

      if (!parsed.length && !hasRootTable && !isEmptyFile) {
        setStatus(t("noItemsDetected"));
        return;
      }

      let categoryMap = null;
      if (await exists(categoriesFile)) {
        try {
          const rawCategories = await readTextFile(categoriesFile);
          categoryMap = JSON.parse(rawCategories);
        } catch (e) {
          categoryMap = null;
        }
      }

      setItems(applyCategoryMap(parsed, categoryMap));
      setFilePath(itemsPath);
      setLoadedFile("items.lua");
      setInventoryRoot(base);
      setImagesDir(imagesPath);
      setCategoriesPath(categoriesFile);
      setSelectedCategory(ALL_CATEGORY);
      setSelectedItemId(parsed[0]?.id || null);
      setSearch("");
      setStatus(t("loadedItems", { count: parsed.length }));
    } catch (e) {
      setStatus(t("loadError", { message: e?.message || String(e) }));
    }
  }

  function updateItem(id, updater) {
    setItems((prev) =>
      prev.map((it) => (it.id === id ? (typeof updater === "function" ? updater(it) : updater) : it))
    );
  }

  function updateDeep(id, path, value) {
    // path z.B. "client.status.thirst"
    updateItem(id, (it) => {
      const copy = structuredClone(it);
      const keys = path.split(".");
      let t = copy;
      for (let i = 0; i < keys.length - 1; i++) {
        if (!isPlainObject(t[keys[i]])) t[keys[i]] = {};
        t = t[keys[i]];
      }
      t[keys[keys.length - 1]] = value;
      return copy;
    });
  }

  async function persistNow(nextItems = null, options = {}) {
    if (!filePath) return;
    try {
      const rootName = "ItemList"; // wir schreiben standardmaessig ItemList
      const next = nextItems || items;
      const lua = buildLuaFromItems(next, rootName, { includeCategoryComments: false });
      await writeTextFile(filePath, lua);
      if (categoriesPath) {
        const categoryMap = buildCategoryMap(next);
        await writeTextFile(categoriesPath, JSON.stringify(categoryMap, null, 2));
      }
      if (!options.silent) {
        showToast(t("saved"));
        setStatus("");
      }
    } catch (e) {
      setStatus(t("saveError", { message: e?.message || String(e) }));
    }
  }

  async function handleApplyAndSave() {
    // items sind schon im State geändert, wir schreiben direkt zurück
    await persistNow();
  }

  async function handleAddImage(updateFn, id) {
    if (!imagesDir) {
      setStatus(t("imagesMissing"));
      return;
    }

    try {
      const selected = await open({
        multiple: false,
        filters: [
          {
            name: "Images",
            extensions: ["png", "jpg", "jpeg", "gif", "webp"],
          },
        ],
      });
      if (!selected) return;
      const filePath = Array.isArray(selected) ? selected[0] : selected;
      const fileName = await basename(filePath);
      const destination = await join(imagesDir, fileName);
      await copyFile(filePath, destination);
      updateFn(id, "image", fileName);
      setStatus("");
      showToast(t("imageAdded"));
    } catch (e) {
      setStatus(t("imageAddError", { message: e?.message || String(e) }));
    }
  }

  function updateDraftDeep(_id, path, value) {
    setNewItemDraft((prev) => {
      if (!prev) return prev;
      const copy = structuredClone(prev);
      const keys = path.split(".");
      let t = copy;
      for (let i = 0; i < keys.length - 1; i++) {
        if (!isPlainObject(t[keys[i]])) t[keys[i]] = {};
        t = t[keys[i]];
      }
      t[keys[keys.length - 1]] = value;
      return copy;
    });
  }

  async function handleCreateItemFromDraft() {
    const id = (newItemDraft?.id || "").trim();
    if (!id) {
      setStatus(t("idRequired"));
      return;
    }
    const existsId = items.some((it) => it.id === id);
    if (existsId) {
      setStatus(t("idExists", { id }));
      return;
    }
    const created = structuredClone(newItemDraft);
    created.id = id;
    if (!created.name) created.name = id;
    if (!created.label) created.label = id;
    const nextItems = [created, ...items];
    setItems(nextItems);
    setSelectedItemId(id);
    setSelectedCategory(created.category || UNCATEGORIZED);
    setNewItemDraft(null);
    setNewItemStage(null);
    setStatus("");
    await persistNow(nextItems, { silent: true });
    showToast(t("itemCreated"));
  }

  function renderField(id, key, value, parentPath = "", updateFn = updateDeep) {
    const fullPath = parentPath ? `${parentPath}.${key}` : key;
    // category editor (wichtig)
    if (key === "category") {
      return (
        <div key={fullPath} className="flex flex-col">
          <label className="text-sm opacity-80 mb-1">{t("category")}</label>
          <div className="flex flex-col gap-2">
            {(() => {
              const options = categoryOptions.includes(value || "")
                ? categoryOptions
                : [...categoryOptions, value].filter(Boolean);
              return (
                <select
                  value={value || UNCATEGORIZED}
                  onChange={(e) => updateFn(id, "category", e.target.value)}
                  className="bg-panel2 text-white p-2 rounded border border-slate-700"
                >
                  {options.map((c) => (
                    <option key={c} value={c}>
                      {displayCategoryName(c)}
                    </option>
                  ))}
                </select>
              );
            })()}
            <input
              type="text"
              placeholder={t("newCategoryPlaceholder")}
              onKeyDown={(e) => {
                if (e.key !== "Enter") return;
                const next = e.currentTarget.value.trim();
                if (!next) return;
                updateFn(id, "category", next);
                e.currentTarget.value = "";
              }}
              className="bg-panel2 text-white p-2 rounded border border-slate-700"
            />
          </div>
        </div>
      );
    }

    // objects -> details
    if (isPlainObject(value)) {
      const isClient = key === "client";
      return (
        <details
          key={fullPath}
          open={isClient}
          className={`col-span-1 bg-panel2 rounded-xl2 p-3 border border-slate-700 ${
            isClient ? "md:col-span-2 lg:col-span-2" : ""
          }`}
        >
          <summary className="cursor-pointer text-accent font-semibold">{key}</summary>
          <div
            className={`grid grid-cols-1 gap-3 mt-2 ${
              isClient ? "" : "md:grid-cols-2"
            }`}
          >
            {Object.entries(value).map(([k, v]) => renderField(id, k, v, fullPath, updateFn))}
          </div>
        </details>
      );
    }

    // arrays
    if (Array.isArray(value)) {
      return (
        <div key={fullPath} className="flex flex-col">
          <label className="text-sm opacity-80 mb-1">{key}</label>
          <textarea
            value={value.join("\n")}
            onChange={(e) => updateFn(id, fullPath, e.target.value.split("\n"))}
            className="bg-panel2 text-white p-2 rounded border border-slate-700 resize-y min-h-[80px]"
          />
        </div>
      );
    }

    // boolean
    if (typeof value === "boolean") {
      const toggleId = `${id}-${fullPath}`.replace(/[^\w-]/g, "_");
      return (
        <div key={fullPath} className="flex items-center justify-between gap-3 mt-4">
          <label htmlFor={toggleId} className="text-sm opacity-80">
            {key}
          </label>
          <label className="relative inline-flex items-center cursor-pointer">
            <input
              id={toggleId}
              type="checkbox"
              checked={value}
              onChange={(e) => updateFn(id, fullPath, e.target.checked)}
              className="sr-only peer"
            />
            <div className="w-11 h-6 bg-slate-700 rounded-full peer peer-checked:bg-accent transition-colors"></div>
            <div className="absolute left-1 top-1 w-4 h-4 bg-white rounded-full transition-transform peer-checked:translate-x-5"></div>
          </label>
        </div>
      );
    }

    // rare dropdown
    if (key.toLowerCase() === "rare") {
      return (
        <div key={fullPath} className="flex flex-col">
          <label className="text-sm opacity-80 mb-1">{key}</label>
          <select
            value={value || "common"}
            onChange={(e) => updateFn(id, fullPath, e.target.value)}
            className="bg-panel2 text-white p-2 rounded border border-slate-700"
          >
            {rareOptions.map((o) => (
              <option key={o} value={o}>
                {o}
              </option>
            ))}
          </select>
        </div>
      );
    }

    // ammoType dropdown for weapon_*
    if (key.toLowerCase() === "ammotype" && id.toLowerCase().startsWith("weapon_")) {
      return (
        <div key={fullPath} className="flex flex-col">
          <label className="text-sm opacity-80 mb-1">{key}</label>
          <select
            value={value === null ? "nil" : value}
            onChange={(e) => updateFn(id, fullPath, e.target.value === "nil" ? null : e.target.value)}
            className="bg-panel2 text-white p-2 rounded border border-slate-700"
          >
            {ammoOptions.map((opt) => (
              <option key={opt} value={opt}>
                {opt}
              </option>
            ))}
          </select>
        </div>
      );
    }

    // vec3 editor
    const vec = parseVec3String(value);
    if (vec) {
      const ctor = vec.ctor.toLowerCase() === "vector3" ? "vector3" : "vec3";
      const updateVec = (axis, axisValue) => {
        const next = {
          ...vec,
          [axis]: Number(axisValue),
        };
        updateFn(id, fullPath, `${ctor}(${next.x}, ${next.y}, ${next.z})`);
      };
      return (
        <div key={fullPath} className="flex flex-col">
          <label className="text-sm opacity-80 mb-1">{key}</label>
          <div className="grid grid-cols-3 gap-2">
            <input
              type="number"
              value={vec.x}
              onChange={(e) => updateVec("x", e.target.value)}
              className="bg-panel2 text-white p-2 rounded border border-slate-700"
            />
            <input
              type="number"
              value={vec.y}
              onChange={(e) => updateVec("y", e.target.value)}
              className="bg-panel2 text-white p-2 rounded border border-slate-700"
            />
            <input
              type="number"
              value={vec.z}
              onChange={(e) => updateVec("z", e.target.value)}
              className="bg-panel2 text-white p-2 rounded border border-slate-700"
            />
          </div>
          <p className="text-xs text-slate-400 mt-1">{ctor}(x, y, z)</p>
        </div>
      );
    }

    // image picker
    if (key === "image") {
      return (
        <div key={fullPath} className="flex flex-col">
          <label className="text-sm opacity-80 mb-1">{key}</label>
          <div className="flex items-center gap-2">
            <input
              type="text"
              value={value ?? ""}
              onChange={(e) => updateFn(id, fullPath, e.target.value)}
              className="bg-panel2 text-white p-2 rounded border border-slate-700 flex-1"
            />
            <button
              type="button"
              onClick={() => handleAddImage(updateFn, id)}
              className="bg-panel2 border border-slate-700 text-slate-200 px-3 py-2 rounded-xl2 hover:bg-slate-800"
            >
              {t("imageAdd")}
            </button>
          </div>
        </div>
      );
    }

    // number
    if (typeof value === "number") {
      return (
        <div key={fullPath} className="flex flex-col">
          <label className="text-sm opacity-80 mb-1">{key}</label>
          <input
            type="number"
            value={value}
            onChange={(e) => updateFn(id, fullPath, Number(e.target.value))}
            className="bg-panel2 text-white p-2 rounded border border-slate-700"
          />
        </div>
      );
    }

    // string/other
    return (
      <div key={fullPath} className="flex flex-col">
        <label className="text-sm opacity-80 mb-1">{key}</label>
        <input
          type="text"
          value={value ?? ""}
          onChange={(e) => updateFn(id, fullPath, e.target.value)}
          className="bg-panel2 text-white p-2 rounded border border-slate-700"
        />
      </div>
    );
  }

  return (
    <div className="h-screen flex flex-col bg-bg text-slate-200 overflow-hidden">
      {/* Header */}
      <header className="flex items-center justify-between p-4 border-b border-slate-800 bg-panel shadow-soft">
        <div>
          <div className="flex items-center gap-3">
            <h1 className="text-2xl font-bold text-accent">Quasar Item Editor</h1>
            <span className="text-xs px-2 py-0.5 rounded-full bg-panel2 border border-slate-700 text-slate-300">
              v{APP_VERSION_LABEL}
            </span>
          </div>
          {loadedFile && (
            <p className="text-sm text-slate-400 mt-1">
              {t("loadedFile")}: <span className="text-slate-200">{loadedFile}</span>
            </p>
          )}
          {inventoryRoot && (
            <p className="text-xs text-slate-500 mt-1">
              {t("inventoryLabel")}: <span className="text-slate-300">{inventoryRoot}</span>
            </p>
          )}
          {status && <p className="text-sm mt-1 text-slate-300">{status}</p>}
          <p className="text-xs text-slate-500 mt-2">
            Made with ❤️ by{" "}
            <a
              href="https://werksfabrik.net"
              onClick={(e) => {
                e.preventDefault();
                openUrl("https://werksfabrik.net");
              }}
              className="text-slate-300 hover:text-accent"
            >
              koichixD
            </a>{" "}
            for the Quasar Store community ✨
          </p>
        </div>

        <div className="flex-1 flex justify-center px-6">
          <div className="relative w-full max-w-[520px]">
            <input
              className="w-full bg-panel2 border border-slate-700 rounded-full px-4 py-2 text-sm shadow-soft focus:border-accent focus:ring-2 focus:ring-accent/30 transition"
              placeholder={t("searchPlaceholder")}
              value={search}
              onChange={(e) => setSearch(e.target.value)}
            />
          </div>
        </div>

        <div className="flex items-center gap-2">
          {filePath && (
            <button
              type="button"
              onClick={() => {
                setNewItemTemplate(itemTemplates[0]?.key || "basic");
                setNewItemStage("template");
              }}
              className="bg-accent text-black px-4 py-2 rounded-xl2 shadow-soft hover:opacity-90"
            >
              {t("newItem")}
            </button>
          )}
          <button
            onClick={handleOpenInventory}
            className="bg-accent text-black px-4 py-2 rounded-xl2 shadow-soft hover:opacity-90"
          >
            {t("inventoryOpen")}
          </button>
          <div className="flex items-center gap-1 bg-panel2 border border-slate-700 rounded-full p-1">
            <button
              type="button"
              onClick={() => setLanguage("de")}
              className={`px-2 py-1 rounded-full text-sm ${
                language === "de" ? "bg-accent text-black" : "hover:bg-slate-800"
              }`}
              title="Deutsch"
            >
              <img src="/flags/de.svg" alt="DE" className="w-5 h-4" />
            </button>
            <button
              type="button"
              onClick={() => setLanguage("en")}
              className={`px-2 py-1 rounded-full text-sm ${
                language === "en" ? "bg-accent text-black" : "hover:bg-slate-800"
              }`}
              title="English"
            >
              <img src="/flags/us.svg" alt="US" className="w-5 h-4" />
            </button>
          </div>
        </div>
      </header>

      {/* 3 columns */}
      <div className="flex flex-1 overflow-hidden">
        {/* Categories */}
        <aside className="w-1/6 min-w-[220px] bg-panel border-r border-slate-800 p-3 overflow-y-auto">
          <div className="mb-3 pb-2 border-b border-slate-800">
            <h2 className="text-accent font-semibold">{t("categories")}</h2>
          </div>
          <ul className="space-y-1">
            {categories.map((cat) => (
              <li
                key={cat}
                onClick={() => {
                  setSelectedCategory(cat);
                  setSelectedItemId(null);
                }}
                className={`cursor-pointer flex items-center gap-2 p-2 rounded border transition-colors ${
                  selectedCategory === cat
                    ? "bg-slate-700/80 ring-1 ring-slate-600 border-slate-600"
                    : "border-slate-700 hover:bg-slate-800"
                }`}
              >
                <span className="truncate">{displayCategoryName(cat)}</span>
                <span className="ml-auto text-xs bg-slate-700/70 text-slate-200 px-2 py-0.5 rounded-full">
                  {categoryCounts[cat] || 0}
                </span>
              </li>
            ))}
          </ul>
        </aside>

        {/* Item list */}
        <aside className="w-1/4 min-w-[280px] bg-panel border-r border-slate-800 p-3 overflow-y-auto">
          <div className="flex items-center justify-between gap-2 mb-2 pb-2 border-b border-slate-800">
            <h2 className="text-accent font-semibold">{displayCategoryName(selectedCategory)}</h2>
            <span className="text-xs text-slate-400 bg-slate-800/60 px-2 py-0.5 rounded-full">
              {filteredItems.length}
            </span>
          </div>

          

          <ul className="space-y-1">
            {filteredItems.map((it) => {
              const imageSrc = getImageSrc(it);
              return (
                <li
                  key={it.id}
                  onClick={() => setSelectedItemId(it.id)}
                  className={`cursor-pointer p-2 rounded border transition-colors ${
                    selectedItemId === it.id
                      ? "bg-slate-700/80 ring-1 ring-slate-600 border-slate-600"
                      : "border-slate-700 hover:bg-slate-800"
                  }`}
                >
                  <div className="flex items-center gap-2">
                    {imageSrc ? (
                      <img src={imageSrc} alt={it.label || it.id} className="w-7 h-7 object-contain" />
                    ) : (
                      <div className="w-7 h-7 rounded bg-slate-700/60" />
                    )}
                    <div className="min-w-0">
                      <div className="text-sm font-medium truncate">{it.label || it.id}</div>
                      <div className="text-xs text-slate-400 truncate">{it.id}</div>
                    </div>
                  </div>
                </li>
              );
            })}
          </ul>
        </aside>

        {/* Editor */}
        <main className="flex-1 bg-panel p-4 overflow-y-auto">
          {!selectedItem ? (
            <p className="text-slate-400">{t("chooseItemHint")}</p>
          ) : (
            <div className="pb-10">
              <div className="flex items-center justify-between gap-4 mb-4">
                <div>
                  <h2 className="text-xl font-semibold text-accent">
                    {selectedItem.label || selectedItem.id}
                  </h2>
                  <p className="text-sm text-slate-400">{selectedItem.id}</p>
                </div>

                <button
                  onClick={handleApplyAndSave}
                  className="bg-accent text-black px-4 py-2 rounded-xl2 shadow-soft hover:opacity-90"
                >
                  {t("applyChanges")}
                </button>
              </div>

              {selectedImageSrc && (
                <div className="mb-4">
                  <img
                    src={selectedImageSrc}
                    alt={selectedItem.label || selectedItem.id}
                    className="w-20 h-20 object-contain bg-panel2 rounded-lg border border-slate-700"
                  />
                </div>
              )}

              <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-2 gap-4">
                {/* Kategorie immer sichtbar */}
                {renderField(selectedItem.id, "category", selectedItem.category)}

                {Object.entries(selectedItem)
                  .filter(([k]) => k !== "id" && k !== "category")
                  .map(([k, v]) => renderField(selectedItem.id, k, v))}
              </div>
            </div>
          )}
        </main>
      </div>

      {newItemStage === "template" && (
        <div className="fixed inset-0 z-50 flex items-center justify-center bg-black/60 p-4">
          <div className="w-full max-w-[420px] bg-panel border border-slate-700 rounded-xl2 p-4">
            <div className="flex items-center justify-between mb-3">
              <h3 className="text-accent font-semibold">{t("selectTemplateTitle")}</h3>
              <button
                type="button"
                onClick={() => setNewItemStage(null)}
                className="text-slate-400 hover:text-slate-200"
              >
                {t("close")}
              </button>
            </div>
            <div className="flex flex-col gap-2">
              {itemTemplates.map((template) => (
                <button
                  key={template.key}
                  type="button"
                  onClick={() => {
                    const category =
                      selectedCategory === ALL_CATEGORY ? UNCATEGORIZED : selectedCategory;
                    const draft = template.build({
                      id: "",
                      label: "",
                      category,
                    });
                    setNewItemTemplate(template.key);
                    setNewItemDraft(draft);
                    setNewItemStage("editor");
                  }}
                  className="text-left bg-panel2 border border-slate-700 rounded px-3 py-2 hover:bg-slate-800"
                >
                  {t(template.labelKey)}
                </button>
              ))}
            </div>
          </div>
        </div>
      )}

      {newItemStage === "editor" && newItemDraft && (
        <div className="fixed inset-0 z-50 flex items-center justify-center bg-black/60 p-4">
          <div className="w-full max-w-[900px] max-h-[90vh] overflow-y-auto bg-panel border border-slate-700 rounded-xl2 p-4">
            <div className="flex items-center justify-between mb-3">
              <h3 className="text-accent font-semibold">{t("createItemTitle")}</h3>
              <button
                type="button"
                onClick={() => {
                  setNewItemDraft(null);
                  setNewItemStage(null);
                }}
                className="text-slate-400 hover:text-slate-200"
              >
                {t("close")}
              </button>
            </div>

            <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-2 gap-4">
              <div className="flex flex-col">
                <label className="text-sm opacity-80 mb-1">{t("itemId")}</label>
                <input
                  type="text"
                  value={newItemDraft.id}
                  onChange={(e) => {
                    const nextId = e.target.value;
                    setNewItemDraft((prev) => {
                      if (!prev) return prev;
                      const copy = structuredClone(prev);
                      const oldId = copy.id;
                      copy.id = nextId;
                      if (copy.name === oldId) copy.name = nextId;
                      if (!copy.label || copy.label === oldId) copy.label = nextId;
                      return copy;
                    });
                  }}
                  className="bg-panel2 text-white p-2 rounded border border-slate-700"
                />
              </div>
            </div>

            <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-2 gap-4 mt-4">
              {Object.entries(newItemDraft)
                .filter(([k]) => k !== "id")
                .map(([k, v]) => (
                  <div key={k}>
                    {renderField(newItemDraft.id || "new", k, v, "", updateDraftDeep)}
                  </div>
                ))}
            </div>

            <div className="flex items-center justify-end gap-2 mt-4">
              <button
                type="button"
                onClick={handleCreateItemFromDraft}
                className="bg-accent text-black px-4 py-2 rounded-xl2 shadow-soft hover:opacity-90"
              >
                {t("create")}
              </button>
              <button
                type="button"
                onClick={() => {
                  setNewItemDraft(null);
                  setNewItemStage(null);
                }}
                className="bg-panel2 border border-slate-700 text-slate-200 px-4 py-2 rounded-xl2 hover:bg-slate-800"
              >
                {t("cancel")}
              </button>
            </div>
          </div>
        </div>
      )}

      {toastMessage && (
        <div className="fixed inset-0 z-50 flex items-center justify-center pointer-events-none">
          <div className="bg-panel border border-slate-700 rounded-xl2 px-5 py-4 shadow-soft flex items-center gap-3">
            <div className="text-accent animate-bounce">
              <svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true">
                <path
                  d="M20 6L9 17l-5-5"
                  stroke="currentColor"
                  strokeWidth="2.5"
                  strokeLinecap="round"
                  strokeLinejoin="round"
                />
              </svg>
            </div>
            <span className="text-slate-200 text-sm">{toastMessage}</span>
          </div>
        </div>
      )}
    </div>
  );
}
