// ============================================================
// Editor - éditeur de code avec coloration syntaxique
// ============================================================

const Icon = ({ name, size = 14, className, style, ...extra }) => {
  const isDiscord = name === "discord";
  const stroke = isDiscord ? "none" : "currentColor";
  const fill = isDiscord ? "currentColor" : "none";
  const strokeWidth = isDiscord ? 0 : 2;
  const props = {
    width: size,
    height: size,
    viewBox: "0 0 24 24",
    fill,
    stroke,
    strokeWidth,
    strokeLinecap: "round",
    strokeLinejoin: "round",
    className,
    style,
    ...extra,
  };
  const paths = {
    logs: <><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><path d="M14 2v6h6"/><path d="M8 13h8M8 17h6"/></>,
    cart: <><circle cx="9" cy="21" r="1"/><circle cx="20" cy="21" r="1"/><path d="M1 1h4l2.7 13.4a2 2 0 0 0 2 1.6h9.7a2 2 0 0 0 2-1.6L23 6H6"/></>,
    folder: <><path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"/></>,
    folderOpen: <><path d="M6 14l1.45-2.9A2 2 0 0 1 9.24 10H20a2 2 0 0 1 1.94 2.5l-1.55 6a2 2 0 0 1-1.94 1.5H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h3.93a2 2 0 0 1 1.66.9l.82 1.2a2 2 0 0 0 1.66.9H18a2 2 0 0 1 2 2v2"/></>,
    file: <><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><path d="M14 2v6h6"/></>,
    image: <><rect x="3" y="3" width="18" height="18" rx="2"/><circle cx="9" cy="9" r="2"/><path d="m21 15-5-5L5 21"/></>,
    code: <><path d="m16 18 6-6-6-6"/><path d="m8 6-6 6 6 6"/></>,
    upload: <><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><path d="m17 8-5-5-5 5"/><path d="M12 3v12"/></>,
    edit: <><path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/><path d="m18.5 2.5 3 3L12 15l-4 1 1-4z"/></>,
    trash: <><path d="M3 6h18"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6"/><path d="M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/></>,
    pencil: <><path d="M17 3a2.85 2.85 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5z"/></>,
    download: <><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><path d="m7 10 5 5 5-5"/><path d="M12 15V3"/></>,
    chevR: <><path d="m9 18 6-6-6-6"/></>,
    chevD: <><path d="m6 9 6 6 6-6"/></>,
    save: <><path d="M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z"/><path d="M17 21v-8H7v8"/><path d="M7 3v5h8"/></>,
    logout: <><path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"/><path d="m16 17 5-5-5-5"/><path d="M21 12H9"/></>,
    search: <><circle cx="11" cy="11" r="8"/><path d="m21 21-4.3-4.3"/></>,
    refresh: <><path d="M3 12a9 9 0 0 1 15-6.7L21 8"/><path d="M21 3v5h-5"/><path d="M21 12a9 9 0 0 1-15 6.7L3 16"/><path d="M3 21v-5h5"/></>,
    eye: <><path d="M2 12s3-7 10-7 10 7 10 7-3 7-10 7-10-7-10-7z"/><circle cx="12" cy="12" r="3"/></>,
    lock: <><rect x="3" y="11" width="18" height="11" rx="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/></>,
    warn: <><path d="m21.7 18-9-15.5a2 2 0 0 0-3.4 0L.3 18A2 2 0 0 0 2 21h18a2 2 0 0 0 1.7-3z"/><path d="M12 9v4"/><path d="M12 17h.01"/></>,
    check: <><path d="M20 6 9 17l-5-5"/></>,
    x: <><path d="M18 6 6 18"/><path d="m6 6 12 12"/></>,
    arrowLeft: <><path d="m15 18-6-6 6-6"/></>,
    plus: <><path d="M12 5v14"/><path d="M5 12h14"/></>,
    discord: <path d="M20.317 4.37a19.791 19.791 0 0 0-4.885-1.515.074.074 0 0 0-.079.037c-.21.375-.444.864-.608 1.25a18.27 18.27 0 0 0-5.487 0 12.64 12.64 0 0 0-.617-1.25.077.077 0 0 0-.079-.037A19.736 19.736 0 0 0 3.677 4.37a.07.07 0 0 0-.032.027C.533 9.046-.32 13.58.099 18.057a.082.082 0 0 0 .031.057 19.9 19.9 0 0 0 5.993 3.03.078.078 0 0 0 .084-.028c.462-.63.874-1.295 1.226-1.994.021-.041.001-.09-.041-.106a13.107 13.107 0 0 1-1.872-.892.077.077 0 0 1-.008-.128 10.2 10.2 0 0 0 .372-.292.074.074 0 0 1 .077-.01c3.929 1.793 8.18 1.793 12.061 0a.074.074 0 0 1 .078.01c.12.098.246.198.373.292a.077.077 0 0 1-.006.127 12.299 12.299 0 0 1-1.873.894.077.077 0 0 0-.041.107c.36.698.772 1.362 1.225 1.993a.076.076 0 0 0 .084.028 19.839 19.839 0 0 0 6.002-3.03.077.077 0 0 0 .032-.054c.5-5.177-.838-9.674-3.549-13.66a.061.061 0 0 0-.031-.028zM8.02 15.33c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.956-2.419 2.157-2.419 1.21 0 2.176 1.096 2.157 2.42 0 1.333-.956 2.418-2.157 2.418zm7.975 0c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.955-2.419 2.157-2.419 1.21 0 2.176 1.096 2.157 2.42 0 1.333-.946 2.418-2.157 2.418z"/>,
    database: <><ellipse cx="12" cy="5" rx="9" ry="3"/><path d="M21 12c0 1.66-4 3-9 3s-9-1.34-9-3"/><path d="M3 5v14c0 1.66 4 3 9 3s9-1.34 9-3V5"/></>,
    copy: <><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></>,
    terminal: <><polyline points="4 17 10 11 4 5"/><line x1="12" y1="19" x2="20" y2="19"/></>,
    link: <><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></>,
    user: <><path d="M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></>,
    menu: <><line x1="3" y1="12" x2="21" y2="12"/><line x1="3" y1="6" x2="21" y2="6"/><line x1="3" y1="18" x2="21" y2="18"/></>,
    calendar: <><rect x="3" y="4" width="18" height="18" rx="2" ry="2"/><line x1="16" y1="2" x2="16" y2="6"/><line x1="8" y1="2" x2="8" y2="6"/><line x1="3" y1="10" x2="21" y2="10"/></>,
    globe: <><circle cx="12" cy="12" r="10"/><line x1="2" y1="12" x2="22" y2="12"/><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/></>,
    camera: <><path d="M23 19a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h4l2-3h6l2 3h4a2 2 0 0 1 2 2z"/><circle cx="12" cy="13" r="4"/></>,
    zoom: <><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/><line x1="11" y1="8" x2="11" y2="14"/><line x1="8" y1="11" x2="14" y2="11"/></>,
    heart: <><path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z"/></>,
    shield: <><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/></>,
    mapPin: <><path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z"/><circle cx="12" cy="10" r="3"/></>,
  };
  return <svg {...props}>{paths[name] || null}</svg>;
};

window.Icon = Icon;

// =================================================================
// Syntax highlighter - basé sur des regex, par langage
// =================================================================
function escapeHtml(s) {
  return s.replace(/[&<>]/g, (c) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;" })[c]);
}

const _PAT = {
  lua: [
    [/--\[\[[\s\S]*?\]\]/, "cmt"],
    [/--[^\n]*/, "cmt"],
    [/\[\[[\s\S]*?\]\]/, "str"],
    [/"(?:\\.|[^"\\\n])*"/, "str"],
    [/'(?:\\.|[^'\\\n])*'/, "str"],
    [/\b(and|break|do|else|elseif|end|false|for|function|goto|if|in|local|nil|not|or|repeat|return|then|true|until|while)\b/, "kw"],
    [/\b(print|require|pairs|ipairs|next|type|tostring|tonumber|setmetatable|getmetatable|select|rawget|rawset|rawequal|error|assert|pcall|xpcall|unpack|table|string|math|os|io|coroutine|debug|RegisterCommand|RegisterNetEvent|AddEventHandler|TriggerEvent|TriggerClientEvent|TriggerServerEvent|CreateThread|Wait|Citizen|MySQL|DropPlayer|GetPlayerPed|PlayerPedId)\b/, "fn"],
    [/[A-Za-z_][\w]*(?=\s*\()/, "call"],
    [/\b[A-Z_][A-Z0-9_]{2,}\b/, "const"],
    [/\b\d+\.?\d*(?:e[+-]?\d+)?\b/i, "num"],
    [/[+\-*/%=<>~^#]+/, "op"],
    [/[{}\[\]\(\),:;.]/, "punc"],
  ],
  json: [
    [/"(?:\\.|[^"\\])*"(?=\s*:)/, "key"],
    [/"(?:\\.|[^"\\])*"/, "str"],
    [/\b(true|false|null)\b/, "kw"],
    [/-?\d+\.?\d*(?:e[+-]?\d+)?/i, "num"],
    [/[{}\[\]]/, "punc"],
    [/[,:]/, "op"],
  ],
  javascript: [
    [/\/\*[\s\S]*?\*\//, "cmt"],
    [/\/\/[^\n]*/, "cmt"],
    [/`(?:\\.|[^`\\])*`/, "str"],
    [/"(?:\\.|[^"\\\n])*"/, "str"],
    [/'(?:\\.|[^'\\\n])*'/, "str"],
    [/\b(var|let|const|function|return|if|else|for|while|do|switch|case|break|continue|new|class|extends|this|super|import|export|from|default|async|await|try|catch|finally|throw|typeof|instanceof|in|of|null|undefined|true|false|void|delete|yield)\b/, "kw"],
    [/\b(console|window|document|Math|Object|Array|String|Number|JSON|Promise|Date|setTimeout|setInterval|fetch|require|module|exports|React)\b/, "fn"],
    [/[A-Za-z_$][\w$]*(?=\s*\()/, "call"],
    [/\b\d+\.?\d*(?:e[+-]?\d+)?\b/i, "num"],
    [/[+\-*/%=<>!&|^~?]+/, "op"],
    [/[{}\[\]\(\),.;:]/, "punc"],
  ],
  ini: [
    [/[;#][^\n]*/, "cmt"],
    [/\/\/[^\n]*/, "cmt"],
    [/\[[^\]\n]+\]/, "sect"],
    [/"(?:\\.|[^"\\\n])*"/, "str"],
    [/'(?:\\.|[^'\\\n])*'/, "str"],
    [/\b(set|sets|setr|sv_[\w]+|endpoint_add_tcp|endpoint_add_udp)\b/, "kw"],
    [/\b\d+\.?\d*\b/, "num"],
    [/=/, "op"],
  ],
  log: [
    [/^\[[^\]\n]+\]/m, "cmt"],
    [/\b(ERROR|FAIL|FATAL)\b/, "err"],
    [/\b(WARN|WARNING)\b/, "warn"],
    [/\b(INFO|OK|SUCCESS)\b/, "kw"],
    [/\b(DEBUG|TRACE)\b/, "punc"],
  ],
};

// pre-compile each pattern with sticky flag
const _PREP = Object.fromEntries(
  Object.entries(_PAT).map(([k, arr]) => [
    k,
    arr.map(([re, t]) => {
      const flags = re.flags.replace(/[gy]/g, "") + "y";
      return [new RegExp(re.source, flags), t];
    }),
  ])
);

function highlight(code, lang) {
  const pats = _PREP[lang];
  if (!pats) return escapeHtml(code);
  let out = "";
  let i = 0;
  const n = code.length;
  while (i < n) {
    let matched = null;
    for (const [re, t] of pats) {
      re.lastIndex = i;
      const m = re.exec(code);
      if (m && m.index === i && m[0].length > 0) {
        matched = { text: m[0], t };
        break;
      }
    }
    if (matched) {
      out += `<span class="tk-${matched.t}">${escapeHtml(matched.text)}</span>`;
      i += matched.text.length;
    } else {
      // Pass through unmatched char (escape it)
      const c = code[i];
      out += c === "&" ? "&amp;" : c === "<" ? "&lt;" : c === ">" ? "&gt;" : c;
      i++;
    }
  }
  return out;
}

// =================================================================
// Code editor - pré coloré derrière, textarea transparent au-dessus
// =================================================================
function CodeEditor({ value, onChange, language = "text", readOnly = false }) {
  const taRef = React.useRef(null);
  const gutterRef = React.useRef(null);
  const hlRef = React.useRef(null);

  const lines = (value || "").split("\n").length;
  const gutter = Array.from({ length: lines }, (_, i) => (i + 1).toString()).join("\n");

  const highlighted = React.useMemo(
    () => highlight(value || "", language) + "\n",
    [value, language]
  );

  const onKeyDown = (e) => {
    if (readOnly) return;
    if (e.key === "Tab") {
      e.preventDefault();
      const ta = e.currentTarget;
      const s = ta.selectionStart, en = ta.selectionEnd;
      const next = value.slice(0, s) + "  " + value.slice(en);
      onChange(next);
      requestAnimationFrame(() => { ta.selectionStart = ta.selectionEnd = s + 2; });
    }
  };

  const onScroll = (e) => {
    const ta = e.target;
    if (gutterRef.current) gutterRef.current.scrollTop = ta.scrollTop;
    if (hlRef.current) {
      hlRef.current.style.transform = `translate(${-ta.scrollLeft}px, ${-ta.scrollTop}px)`;
    }
  };

  return (
    <div className="editor-area">
      <pre className="editor-gutter" ref={gutterRef}>{gutter}</pre>
      <div className="editor-stack">
        <pre
          className="editor-hl"
          ref={hlRef}
          dangerouslySetInnerHTML={{ __html: highlighted }}
        />
        <textarea
          ref={taRef}
          className="editor-ta"
          value={value}
          onChange={(e) => onChange(e.target.value)}
          onKeyDown={onKeyDown}
          onScroll={onScroll}
          spellCheck={false}
          readOnly={readOnly}
          wrap="off"
        />
      </div>
    </div>
  );
}

window.CodeEditor = CodeEditor;
