/* ============================================================
   Админка · App
   Состояние:
     content   — текущий редактируемый черновик
     published — последняя опубликованная версия (для dirty-сравнения)
     dirty     — true если черновик отличается от published
     authState — 'checking' | 'login' | 'ready'
   API:
     GET  /api/me        — проверка cookie-сессии
     POST /api/login     — логин (получаем httpOnly cookie)
     POST /api/logout    — разлогин
     GET  /api/content   — текущий контент с сервера
     PUT  /api/content   — сохранить черновик
     POST /api/preview   — собрать в /preview/ для просмотра
     POST /api/publish   — выкатить на боевой сайт + бэкап
     GET  /api/backups   — список снапшотов content.json
     POST /api/restore   — восстановить content.json из бэкапа
   ============================================================ */
const { useState, useEffect, useRef } = React;

const api = async (url, opts = {}) => {
  const r = await fetch(url, {
    credentials: 'same-origin',
    headers: opts.body ? { 'Content-Type': 'application/json' } : undefined,
    ...opts
  });
  if (r.status === 401) {
    // Сессия истекла или нет — выкидываем наверх
    const e = new Error('unauthorized'); e.status = 401; throw e;
  }
  if (!r.ok) {
    const j = await r.json().catch(() => ({}));
    throw new Error(j.error || ('HTTP ' + r.status));
  }
  if (r.status === 204) return null;
  const ct = r.headers.get('content-type') || '';
  return ct.includes('application/json') ? r.json() : r.text();
};

const LS_DRAFT = 'atp_draft_v2';  // кеш черновика (восстановление при перезагрузке)
const loadDraft = () => { try { return JSON.parse(localStorage.getItem(LS_DRAFT)); } catch { return null; } };
const saveDraft = (c) => { try { localStorage.setItem(LS_DRAFT, JSON.stringify(c)); } catch {} };
const clearDraft = () => { try { localStorage.removeItem(LS_DRAFT); } catch {} };

function App() {
  const [authState, setAuthState] = useState('checking');
  const [content, setContent] = useState(null);
  const [published, setPublished] = useState(null);
  const [active, setActive] = useState('settings');
  const [dirty, setDirty] = useState(false);
  const [toast, setToast] = useState(null);
  const [busy, setBusy] = useState(false);
  const [showHist, setShowHist] = useState(false);
  const [backups, setBackups] = useState([]);
  const [navOpen, setNavOpen] = useState(false);
  const toastT = useRef();

  const flash = (msg, kind='ok') => {
    setToast({ msg, kind });
    clearTimeout(toastT.current);
    toastT.current = setTimeout(() => setToast(null), 2600);
  };

  // На mount: проверяем cookie + грузим контент
  useEffect(() => { (async () => {
    try {
      await api('/api/me');
      const c = await api('/api/content');
      const draft = loadDraft();
      if (draft && JSON.stringify(draft) !== JSON.stringify(c)) {
        // На клиенте есть несохранённый черновик — спросим юзера
        if (confirm('Есть несохранённые правки с прошлой сессии. Восстановить?')) {
          setContent(draft);
          setDirty(true);
        } else {
          clearDraft();
          setContent(c);
        }
      } else {
        setContent(c);
      }
      setPublished(c);
      setAuthState('ready');
    } catch (e) {
      if (e.status === 401) setAuthState('login');
      else { console.error(e); setAuthState('login'); }
    }
  })(); }, []);

  // Кешируем черновик в localStorage по мере изменений
  useEffect(() => { if (content) saveDraft(content); }, [content]);

  // Активная секция (защита: первая существующая если active = битый)
  const sec = window.SCHEMA.find(s => s.id === active) || window.SCHEMA[0];

  const setField = (key, val) => {
    setContent(c => {
      const next = window.deepClone(c);
      const sectionObj = next[sec.id] || (next[sec.id] = {});
      sectionObj[key] = typeof val === 'function' ? val(sectionObj[key]) : val;
      return next;
    });
    setDirty(true);
  };

  // Per-section dirty (для индикатора-точки в sidebar)
  const secDirty = (id) => {
    if (!published) return false;
    return JSON.stringify(content[id]) !== JSON.stringify(published[id]);
  };

  const onSave = async () => {
    if (busy) return;
    setBusy(true);
    try {
      await api('/api/content', { method: 'PUT', body: JSON.stringify(content) });
      flash('Черновик сохранён');
      clearDraft();
    } catch (e) { flash('Ошибка: ' + e.message, 'err'); }
    finally { setBusy(false); }
  };

  const onPublish = async () => {
    if (busy) return;
    if (!confirm('Опубликовать изменения на сайт?')) return;
    setBusy(true);
    try {
      await api('/api/content', { method: 'PUT', body: JSON.stringify(content) });
      const r = await api('/api/publish', { method: 'POST' });
      setPublished(window.deepClone(content));
      setDirty(false);
      clearDraft();
      flash('Опубликовано · сайт обновлён');
      // обновляем список бэкапов в фоне (на случай если ящик истории открыт)
      api('/api/backups').then(setBackups).catch(()=>{});
    } catch (e) { flash('Ошибка: ' + e.message, 'err'); }
    finally { setBusy(false); }
  };

  const onPreview = async () => {
    if (busy) return;
    setBusy(true);
    try {
      await api('/api/content', { method: 'PUT', body: JSON.stringify(content) });
      const r = await api('/api/preview', { method: 'POST' });
      window.open(r.url, '_blank', 'noopener');
    } catch (e) { flash('Ошибка: ' + e.message, 'err'); }
    finally { setBusy(false); }
  };

  const openHistory = async () => {
    setShowHist(true);
    try { setBackups(await api('/api/backups')); }
    catch (e) { flash('Ошибка истории: ' + e.message, 'err'); }
  };

  const restoreBackup = async (name) => {
    if (!confirm('Восстановить версию ' + name + '? Текущий черновик будет заменён.')) return;
    try {
      await api('/api/restore', { method: 'POST', body: JSON.stringify({ name }) });
      const c = await api('/api/content');
      setContent(c); setDirty(true); setShowHist(false);
      flash('Версия восстановлена в черновик');
    } catch (e) { flash('Ошибка: ' + e.message, 'err'); }
  };

  const onLogout = async () => {
    try { await api('/api/logout', { method: 'POST' }); } catch {}
    clearDraft();
    location.reload();
  };

  /* ---------- LOGIN ---------- */
  if (authState === 'checking') return <div className="boot"><span className="spin spin--big"></span></div>;
  if (authState === 'login') return <LoginScreen onSuccess={() => location.reload()} />;
  if (!content) return <div className="boot"><span className="spin spin--big"></span></div>;

  /* ---------- READY ---------- */
  return (
    <div className="app">
      {/* sidebar */}
      <aside className={"sidebar" + (navOpen ? ' open' : '')}>
        <div className="brandbox">
          <div className="b1">alinatrocenko.pro</div>
          <div className="b2">Админка контента</div>
        </div>
        <nav className="navlist">
          {window.SCHEMA.map(s => (
            <button key={s.id}
                    className={"navitem" + (active===s.id?' active':'') + (secDirty(s.id)?' dirty':'')}
                    onClick={() => { setActive(s.id); setNavOpen(false); }}>
              <Icon n={s.icon} w={18}/> {s.label}<span className="dot"></span>
            </button>
          ))}
        </nav>
        <div className="sidefoot">
          <span className="avatar">А</span>
          <div style={{flex:1,minWidth:0}}>
            <div style={{color:'#ededeb',fontWeight:600,fontSize:13}}>Алина</div>
            <div>Редактор</div>
          </div>
          <button className="logout-side" onClick={onLogout} title="Выйти">⎋</button>
        </div>
      </aside>
      {navOpen && <div className="scrim-m" onClick={() => setNavOpen(false)}></div>}

      {/* topbar */}
      <header className="topbar">
        <div className="tb-left">
          <button className="iconbtn menu-toggle" onClick={() => setNavOpen(true)} title="Меню"><Icon n="menu"/></button>
          <span className="tb-title">{sec.label}</span>
          <span className={"status " + (dirty ? 'draft' : 'published')}><i></i>{dirty ? 'Есть несохранённые правки' : 'Сайт актуален'}</span>
        </div>
        <div className="tb-right">
          <button className="iconbtn" title="История публикаций" onClick={openHistory}><Icon n="history"/></button>
          <button className="btn" onClick={onPreview} disabled={busy}><Icon n="eye"/><span>Предпросмотр</span></button>
          <button className="btn" onClick={onSave} disabled={busy || !dirty}><Icon n="check"/><span>Сохранить</span></button>
          <button className="btn btn--primary" onClick={onPublish} disabled={busy || !dirty}><Icon n="rocket"/><span>Опубликовать</span></button>
        </div>
      </header>

      {/* main */}
      <div className="main">
        <div className="editor">
          <div className="sec-head">
            <h1>{sec.label}</h1>
            <p>{secHint(active)}</p>
          </div>
          <div className="card">
            {sec.fields.map(f => (
              <FieldRenderer key={f.key}
                             f={f}
                             val={(content[sec.id] || {})[f.key]}
                             on={(v) => setField(f.key, v)} />
            ))}
          </div>
        </div>
      </div>

      {/* history drawer */}
      {showHist && (
        <div className="overlay" onClick={e => { if (e.target.className === 'overlay') setShowHist(false); }}>
          <div className="drawer">
            <div className="drawer__head">
              <h3>История публикаций</h3>
              <button className="iconbtn" onClick={() => setShowHist(false)}><Icon n="x"/></button>
            </div>
            <div className="drawer__body">
              {backups.length === 0 && (
                <p style={{color:'#71716e',padding:'20px 8px'}}>Пока нет сохранённых публикаций.</p>
              )}
              {backups.map((name, i) => {
                // Имя файла: content-2026-06-01T11-20-30-123Z.json
                const m = name.match(/content-(\d{4}-\d{2}-\d{2})T(\d{2})-(\d{2})/);
                const at = m ? `${m[1]} · ${m[2]}:${m[3]}` : name;
                return (
                  <div className="ver" key={name}>
                    <Icon n="clock" w={18}/>
                    <div className="vmeta">
                      <div className="vt">Публикация {backups.length - i} {i === 0 && <span className="badge-cur">· последняя</span>}</div>
                      <div className="vd">{at}</div>
                    </div>
                    <button className="btn btn--sm" onClick={() => restoreBackup(name)}>Вернуть</button>
                  </div>
                );
              })}
            </div>
          </div>
        </div>
      )}

      {toast && (
        <div className="toast-wrap">
          <div className={"toast " + (toast.kind === 'err' ? 'toast--err' : 'toast--ok')}>
            <Icon n={toast.kind === 'err' ? 'x' : 'check'}/> {toast.msg}
          </div>
        </div>
      )}
    </div>
  );
}

/* ---------- LOGIN SCREEN ---------- */
function LoginScreen({ onSuccess }) {
  const [pwd, setPwd] = useState('');
  const [err, setErr] = useState('');
  const [busy, setBusy] = useState(false);
  const submit = async () => {
    setErr(''); setBusy(true);
    try {
      await api('/api/login', { method: 'POST', body: JSON.stringify({ password: pwd }) });
      onSuccess();
    } catch (e) { setErr(e.message || 'Ошибка входа'); setBusy(false); }
  };
  return (
    <div className="login">
      <div className="login-card">
        <div className="login-brand">alinatrocenko.pro</div>
        <h1>Админка</h1>
        <p>Введите пароль для доступа</p>
        <input className="inp" type="password" placeholder="Пароль" autoFocus
               value={pwd} onChange={e => setPwd(e.target.value)}
               onKeyDown={e => { if (e.key === 'Enter') submit(); }} />
        {err && <div className="err-line" style={{marginTop:8}}>{err}</div>}
        <button className="btn btn--primary" style={{width:'100%',marginTop:14,justifyContent:'center'}} onClick={submit} disabled={busy}>
          {busy ? 'Входим…' : 'Войти'}
        </button>
      </div>
    </div>
  );
}

function secHint(id) {
  return ({
    settings: 'Глобальные данные сайта, контакты, SEO и превью при шеринге ссылки.',
    hero:     'Первый экран: видео, заголовок, кнопки. Видео запускается автоматически.',
    band:     'Тёмная полоса-манифест: фоновое фото + опциональные слова по углам.',
    work:     'Галерея работ. Перетаскивай для порядка, добавляй и удаляй.',
    packages: 'Пакеты съёмок: цены, что входит, ссылка для записи. Самое частое обновление.',
    process:  'Этапы работы — 4 коротких пункта.',
    about:    'Блок «О себе»: портрет, лид и теги направлений.',
    contact:  'Финальный блок с призывом и контактами.'
  })[id] || '';
}

ReactDOM.createRoot(document.getElementById('root')).render(<App/>);
