// editor.jsx — Board post editor.
//
// Two-column workspace: a form on the left to edit headline/subtitle and
// the 8 member entries (text + photo upload), and a sticky preview on the
// right that scales the live 1200×1200 post to 540×540 for display. A
// hidden, unscaled clone of the post sits off-screen and is what gets
// passed to html-to-image at download time, so the exported PNG is a
// pixel-clean 2400×2400 (pixelRatio 2 over the 1200 source).
//
// Everything persists to localStorage on every change — refresh-safe.
// Uploaded photos are downscaled to ~800px and re-encoded as JPEG before
// being stored, so eight portraits still fit comfortably under the 5MB
// localStorage cap.

const { useState, useEffect, useRef, useCallback } = React;

const STORAGE_KEY = 'fenja-board-data-v1';
const MIGRATION_KEY = 'fenja-board-migrations';

// Apply one-time data migrations. Each entry runs once per browser.
function applyMigrations(parsed) {
  let applied = [];
  try { applied = JSON.parse(localStorage.getItem(MIGRATION_KEY) || '[]'); } catch {}

  // 2026-05-swap-34-67: user asked to swap positions 3↔6 and 4↔7.
  if (!applied.includes('2026-05-swap-34-67') && parsed?.members?.length === 8) {
    const m = parsed.members.slice();
    [m[2], m[5]] = [m[5], m[2]]; // index 2 ↔ 5  (positions 3 ↔ 6)
    [m[3], m[6]] = [m[6], m[3]]; // index 3 ↔ 6  (positions 4 ↔ 7)
    parsed = { ...parsed, members: m };
    applied.push('2026-05-swap-34-67');
  }

  try { localStorage.setItem(MIGRATION_KEY, JSON.stringify(applied)); } catch {}
  return parsed;
}

const DEFAULT_DATA = {
  headlineBefore: 'Meet the Fenja AI',
  headlineEm: 'Advisory Board',
  subtitle: 'Bridging Industry & Sovereign AI',
  members: [
    { name: '[ Full Name ]', title: 'Former CTO',         company: '[ Company ]',     photo: null },
    { name: '[ Full Name ]', title: 'Professor',          company: '[ Institution ]', photo: null },
    { name: '[ Full Name ]', title: 'CEO',                company: '[ Company ]',     photo: null },
    { name: '[ Full Name ]', title: 'Head of Research',   company: '[ Institute ]',   photo: null },
    { name: '[ Full Name ]', title: 'Partner',            company: '[ Firm ]',        photo: null },
    { name: '[ Full Name ]', title: 'CEO',                company: '[ Company ]',     photo: null },
    { name: '[ Full Name ]', title: 'Chief Scientist',    company: '[ Company ]',     photo: null },
    { name: '[ Full Name ]', title: 'Founder',            company: '[ Company ]',     photo: null },
  ],
};

// ────────────────────────────────────────────────────────────────────────
//  Image compression — JPEG at max 800px to keep localStorage happy
// ────────────────────────────────────────────────────────────────────────
async function compressImage(file, maxDim = 800, quality = 0.88) {
  const url = URL.createObjectURL(file);
  try {
    const img = new Image();
    await new Promise((res, rej) => { img.onload = res; img.onerror = rej; img.src = url; });
    const scale = Math.min(1, maxDim / Math.max(img.width, img.height));
    const w = Math.round(img.width * scale);
    const h = Math.round(img.height * scale);
    const canvas = document.createElement('canvas');
    canvas.width = w; canvas.height = h;
    const ctx = canvas.getContext('2d');
    ctx.imageSmoothingEnabled = true;
    ctx.imageSmoothingQuality = 'high';
    ctx.drawImage(img, 0, 0, w, h);
    return canvas.toDataURL('image/jpeg', quality);
  } finally {
    URL.revokeObjectURL(url);
  }
}

// ────────────────────────────────────────────────────────────────────────
//  The post itself — mirrors Variation A in index.html. Renders into a
//  1200×1200 box; CSS lives in editor.html so it applies to both the
//  visible scaled preview and the hidden capture host.
// ────────────────────────────────────────────────────────────────────────
function BoardPost({ data }) {
  return (
    <div className="post">
      <div className="a-root">
        <div className="a-head">
          <h1>
            {data.headlineBefore}{data.headlineBefore && data.headlineEm ? ' ' : ''}
            {data.headlineEm ? <em>{data.headlineEm}</em> : null}
          </h1>
          {data.subtitle ? <div className="subtitle">{data.subtitle}</div> : null}
        </div>

        <div className="a-grid">
          {data.members.map((m, i) => (
            <div key={i} className="member">
              {m.photo
                ? <img className="portrait" src={m.photo} alt="" />
                : <div className="portrait-empty">Portrait</div>}
              <div className="name">{m.name}</div>
              <div className="title">{m.title}</div>
              <div className="company">{m.company}</div>
            </div>
          ))}
        </div>

        <div className="a-foot">
          <div className="mark">
            <img src="assets/fenja-logo-full.png" alt="Fenja AI" />
          </div>
        </div>
      </div>
    </div>
  );
}

// ────────────────────────────────────────────────────────────────────────
//  Editor
// ────────────────────────────────────────────────────────────────────────
function EditorApp() {
  const [data, setData] = useState(() => {
    try {
      const saved = localStorage.getItem(STORAGE_KEY);
      if (saved) {
        let parsed = JSON.parse(saved);
        parsed = applyMigrations(parsed);
        // Merge with defaults to handle schema additions
        return {
          ...DEFAULT_DATA,
          ...parsed,
          members: parsed.members && parsed.members.length === 8
            ? parsed.members
            : DEFAULT_DATA.members,
        };
      }
    } catch {}
    return DEFAULT_DATA;
  });

  const [downloading, setDownloading] = useState(false);
  const captureRef = useRef(null);

  // Persist on every change
  useEffect(() => {
    try {
      localStorage.setItem(STORAGE_KEY, JSON.stringify(data));
    } catch (err) {
      console.warn('localStorage save failed', err);
    }
  }, [data]);

  const updateField = (key, value) => setData(d => ({ ...d, [key]: value }));
  const updateMember = (i, key, value) => setData(d => ({
    ...d,
    members: d.members.map((m, idx) => idx === i ? { ...m, [key]: value } : m),
  }));

  const onPhoto = async (i, file) => {
    if (!file) return;
    try {
      const dataUrl = await compressImage(file);
      updateMember(i, 'photo', dataUrl);
    } catch (err) {
      console.error('Image processing failed', err);
      alert('Could not read that image. Try another file?');
    }
  };

  const onDownload = async () => {
    if (!captureRef.current) return;
    setDownloading(true);
    try {
      // Make sure fonts are loaded before capture, otherwise the headline
      // falls back to Times in the rendered PNG.
      if (document.fonts && document.fonts.ready) {
        await document.fonts.ready;
      }
      const dataUrl = await window.htmlToImage.toPng(captureRef.current, {
        pixelRatio: 2,
        width: 1200,
        height: 1200,
        cacheBust: true,
        backgroundColor: '#faf6ee',
      });
      const link = document.createElement('a');
      link.download = `fenja-advisory-board-${new Date().toISOString().slice(0,10)}.png`;
      link.href = dataUrl;
      document.body.appendChild(link);
      link.click();
      document.body.removeChild(link);
    } catch (err) {
      console.error('Export failed', err);
      alert('Export failed. Check the console for details.');
    } finally {
      setDownloading(false);
    }
  };

  const onReset = () => {
    if (confirm('Reset to defaults? Your edits and uploaded photos will be cleared.')) {
      setData(DEFAULT_DATA);
    }
  };

  return (
    <>
      <header className="topbar">
        <div className="brand">
          <img src="assets/fenja-logo-full.png" alt="Fenja AI" style={{ height: 56, marginLeft: -14 }} />
          <div className="title">Board post <em>editor</em></div>
        </div>
        <div className="actions">
          <a className="btn btn-ghost" href="index.html">Compare layouts</a>
          <button className="btn btn-ghost" onClick={onReset}>Reset</button>
          <button className="btn btn-primary" onClick={onDownload} disabled={downloading}>
            {downloading ? 'Rendering…' : 'Download PNG'}
          </button>
        </div>
      </header>

      <main className="workspace">
        <section className="form-col">
          <h2>Edit the post.</h2>
          <p className="col-sub">Fill in the headline, subtitle, and the eight members. The preview updates as you type.</p>

          <div className="section">
            <h3>Headline</h3>
            <div className="grid-2">
              <div className="field">
                <label>Opening</label>
                <input
                  type="text"
                  value={data.headlineBefore}
                  onChange={e => updateField('headlineBefore', e.target.value)}
                  placeholder="Meet the Fenja AI"
                />
              </div>
              <div className="field">
                <label>Italicized closer</label>
                <input
                  type="text"
                  value={data.headlineEm}
                  onChange={e => updateField('headlineEm', e.target.value)}
                  placeholder="Advisory Board"
                />
                <div className="hint">The terminal phrase, rendered in serif italic bold.</div>
              </div>
            </div>
            <div className="field" style={{ marginTop: 18 }}>
              <label>Subtitle</label>
              <input
                type="text"
                value={data.subtitle}
                onChange={e => updateField('subtitle', e.target.value)}
                placeholder="Bridging Industry & Sovereign AI"
              />
            </div>
          </div>

          <div className="section">
            <h3>Members · 8 portraits</h3>
            {data.members.map((m, i) => (
              <div key={i} className="member-row">
                <div>
                  <div className="number">{String(i + 1).padStart(2, '0')}</div>
                  <label className={`dropzone ${m.photo ? 'has-image' : ''}`}>
                    {m.photo
                      ? <img src={m.photo} alt="" />
                      : <span className="ph">Drop or click<br/>to add photo</span>}
                    <input
                      type="file"
                      accept="image/png,image/jpeg,image/webp"
                      onChange={e => onPhoto(i, e.target.files?.[0])}
                    />
                    {m.photo && (
                      <button
                        className="clear"
                        type="button"
                        onClick={e => { e.preventDefault(); updateMember(i, 'photo', null); }}
                        aria-label="Remove photo"
                      >×</button>
                    )}
                  </label>
                </div>
                <div className="member-fields">
                  <div className="field full">
                    <label>Name</label>
                    <input
                      type="text"
                      value={m.name}
                      onChange={e => updateMember(i, 'name', e.target.value)}
                    />
                  </div>
                  <div className="field">
                    <label>Title</label>
                    <input
                      type="text"
                      value={m.title}
                      onChange={e => updateMember(i, 'title', e.target.value)}
                    />
                  </div>
                  <div className="field">
                    <label>Company</label>
                    <input
                      type="text"
                      value={m.company}
                      onChange={e => updateMember(i, 'company', e.target.value)}
                    />
                  </div>
                </div>
              </div>
            ))}
          </div>
        </section>

        <aside className="preview-col">
          <h3>
            Preview
            <span className="pixel-tag">1200 × 1200 · PNG</span>
          </h3>
          <div className="preview-frame">
            <div className="stage">
              <BoardPost data={data} />
            </div>
          </div>
          <p className="preview-foot">
            The exported PNG renders at 2× pixel density (2400 × 2400) so the type stays crisp after LinkedIn re-encodes.
          </p>
        </aside>
      </main>

      {/* Hidden full-size capture target — what html-to-image actually reads. */}
      <div className="capture-host" aria-hidden="true">
        <div ref={captureRef}>
          <BoardPost data={data} />
        </div>
      </div>
    </>
  );
}
