/* global React */
// Reusable form primitives — inputs, options, dropzone, etc.

const { useState, useRef, useEffect } = React;

function Field({ label, hint, required, children }) {
  return (
    <div className="field">
      {label ? (
        <label className="field__label">
          {label}{required ? <span className="req">*</span> : null}
        </label>
      ) : null}
      {hint ? <div className="field__hint">{hint}</div> : null}
      {children}
    </div>
  );
}

function TextInput({ value, onChange, placeholder, type = "text" }) {
  return (
    <input
      className="input"
      type={type}
      value={value || ""}
      onChange={(e) => onChange(e.target.value)}
      placeholder={placeholder}
    />
  );
}

function TextArea({ value, onChange, placeholder, rows = 4 }) {
  return (
    <textarea
      className="textarea"
      value={value || ""}
      onChange={(e) => onChange(e.target.value)}
      placeholder={placeholder}
      rows={rows}
    />
  );
}

function Select({ value, onChange, options, placeholder }) {
  return (
    <select className="select" value={value || ""} onChange={(e) => onChange(e.target.value)}>
      {placeholder ? <option value="">{placeholder}</option> : null}
      {options.map((o) => (
        <option key={o} value={o}>{o}</option>
      ))}
    </select>
  );
}

function RadioOptions({ value, onChange, options, cols = 1 }) {
  return (
    <div className={`options cols-${cols}`}>
      {options.map((o) => {
        const val = typeof o === "string" ? o : o.value;
        const label = typeof o === "string" ? o : o.label;
        const hint = typeof o === "string" ? null : o.hint;
        const selected = value === val;
        return (
          <label key={val} className={`option${selected ? " is-selected" : ""}`}>
            <input
              type="radio"
              checked={selected}
              onChange={() => onChange(val)}
            />
            <span className="option__dot" />
            <span className="option__text">
              <span className="option__label">{label}</span>
              {hint ? <span className="option__hint">{hint}</span> : null}
            </span>
          </label>
        );
      })}
    </div>
  );
}

function CheckOptions({ value, onChange, options, cols = 1 }) {
  const arr = Array.isArray(value) ? value : [];
  const toggle = (v) => {
    if (arr.includes(v)) onChange(arr.filter((x) => x !== v));
    else onChange([...arr, v]);
  };
  return (
    <div className={`options cols-${cols}`}>
      {options.map((o) => {
        const val = typeof o === "string" ? o : o.value;
        const label = typeof o === "string" ? o : o.label;
        const hint = typeof o === "string" ? null : o.hint;
        const selected = arr.includes(val);
        return (
          <label key={val} className={`option checkbox${selected ? " is-selected" : ""}`}>
            <input type="checkbox" checked={selected} onChange={() => toggle(val)} />
            <span className="option__dot" />
            <span className="option__text">
              <span className="option__label">{label}</span>
              {hint ? <span className="option__hint">{hint}</span> : null}
            </span>
          </label>
        );
      })}
    </div>
  );
}

function ToneSlider({ value, onChange, leftLabel, rightLabel }) {
  return (
    <div className="tone-slider">
      <div className="tone-slider__row">
        <div className="tone-slider__label">{leftLabel}</div>
        <input
          type="range"
          min="0"
          max="100"
          step="5"
          value={value ?? 50}
          onChange={(e) => onChange(Number(e.target.value))}
        />
        <div className="tone-slider__label right">{rightLabel}</div>
      </div>
    </div>
  );
}

const COLOR_PRESETS = [
  { name: "Cobalt",   value: "#1E9CFF", text: "dark" },
  { name: "Midnight", value: "#0A1733", text: "light" },
  { name: "Slate",    value: "#334268", text: "light" },
  { name: "Emerald",  value: "#2BB673", text: "light" },
  { name: "Sand",     value: "#D9C39A", text: "dark" },
  { name: "Crimson",  value: "#C03A3A", text: "light" },
  { name: "Plum",     value: "#6B3F8A", text: "light" },
  { name: "Forest",   value: "#1F5C45", text: "light" },
  { name: "Charcoal", value: "#1A1A1A", text: "light" },
  { name: "Cream",    value: "#F4ECDD", text: "dark" },
];

function HexColorInput({ value, onChange }) {
  // Lightweight hex input: type any hex, native picker live-previews. Normalizes
  // missing # and uppercases for consistency in the payload.
  const [draft, setDraft] = useState(value || "");
  useEffect(() => { setDraft(value || ""); }, [value]);

  const norm = (raw) => {
    let s = (raw || "").trim().replace(/^#?/, "");
    if (!/^[0-9a-f]{0,8}$/i.test(s)) return null;
    if (s.length === 3) s = s.split("").map((c) => c + c).join("");
    if (s.length !== 6 && s.length !== 8) return null;
    return "#" + s.toUpperCase();
  };

  const valid = norm(draft);
  const swatch = valid || "#E7E9EE";

  return (
    <div className="hex-row">
      <label className="hex-row__swatch" style={{ background: swatch }} title="Klicken für Farb-Picker">
        <input
          type="color"
          value={valid || "#1E9CFF"}
          onChange={(e) => { setDraft(e.target.value); onChange(e.target.value.toUpperCase()); }}
          aria-label="Farb-Picker öffnen"
        />
      </label>
      <input
        className="input hex-row__input"
        type="text"
        value={draft}
        onChange={(e) => {
          setDraft(e.target.value);
          const n = norm(e.target.value);
          if (n) onChange(n);
        }}
        onBlur={() => { const n = norm(draft); if (n) { setDraft(n); onChange(n); } }}
        placeholder="#1E9CFF"
        spellCheck={false}
        autoComplete="off"
        autoCapitalize="characters"
      />
      {!valid && draft.length > 0 ? (
        <span className="hex-row__warn">Ungültig</span>
      ) : null}
    </div>
  );
}

function ColorPicker({ value, onChange }) {
  const isPreset = COLOR_PRESETS.some((c) => c.value.toLowerCase() === (value || "").toLowerCase());
  const customColor = value && !isPreset ? value : "#1E9CFF";
  return (
    <div>
      <div className="color-grid">
        {COLOR_PRESETS.map((c) => (
          <div
            key={c.value}
            className={`color-swatch${value === c.value ? " is-selected" : ""}${c.text === "dark" ? " dark" : ""}`}
            style={{ background: c.value }}
            onClick={() => onChange(c.value)}
            title={c.name}
          >
            <span className="color-swatch__name">{c.name}</span>
          </div>
        ))}
      </div>
      <div className="color-custom">
        <label className="color-custom__chip" style={{ background: customColor }}>
          <input
            type="color"
            value={customColor}
            onChange={(e) => onChange(e.target.value)}
          />
        </label>
        <div className="field__hint" style={{ margin: 0 }}>
          …oder eigene Farbe wählen{value && !isPreset ? ` (${value})` : ""}
        </div>
      </div>
    </div>
  );
}

function formatBytes(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 fileExt(name) {
  const i = name.lastIndexOf(".");
  return i >= 0 ? name.slice(i + 1).toUpperCase() : "FILE";
}

function Dropzone({ files, onChange, accept, multiple = true, title, hint }) {
  const [drag, setDrag] = useState(false);
  const inputRef = useRef(null);
  const handleFiles = (list) => {
    const arr = Array.from(list || []);
    if (!arr.length) return;
    onChange(multiple ? [...(files || []), ...arr] : arr.slice(0, 1));
  };
  const removeAt = (i) => onChange((files || []).filter((_, idx) => idx !== i));
  return (
    <div>
      <div
        className={`dropzone${drag ? " is-drag" : ""}`}
        onDragOver={(e) => { e.preventDefault(); setDrag(true); }}
        onDragLeave={() => setDrag(false)}
        onDrop={(e) => {
          e.preventDefault();
          setDrag(false);
          handleFiles(e.dataTransfer.files);
        }}
      >
        <input
          ref={inputRef}
          type="file"
          accept={accept}
          multiple={multiple}
          onChange={(e) => handleFiles(e.target.files)}
        />
        <div className="dropzone__icon">
          <svg viewBox="0 0 24 24"><path d="M12 16V4m0 0l-4 4m4-4l4 4M4 16v2a2 2 0 002 2h12a2 2 0 002-2v-2" strokeLinecap="round" strokeLinejoin="round"/></svg>
        </div>
        <div className="dropzone__title">
          {title || "Dateien hier ablegen"} <span className="dropzone__link">oder durchsuchen</span>
        </div>
        <div className="dropzone__hint">{hint || "PNG, JPG, PDF, SVG · max. 10 MB"}</div>
      </div>
      {(files || []).length > 0 ? (
        <div className="file-list">
          {(files || []).map((f, i) => (
            <div key={i} className="file-row">
              <div className="file-row__icon">{fileExt(f.name)}</div>
              <div className="file-row__name">{f.name}</div>
              <div className="file-row__size">{formatBytes(f.size)}</div>
              <button type="button" className="file-row__remove" onClick={() => removeAt(i)} aria-label="Entfernen">✕</button>
            </div>
          ))}
        </div>
      ) : null}
    </div>
  );
}

Object.assign(window, { Field, TextInput, TextArea, Select, RadioOptions, CheckOptions, ToneSlider, ColorPicker, HexColorInput, Dropzone, formatBytes });
