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

window.addEventListener('error', (e) => {
  const box = document.createElement('div');
  box.style = 'position:fixed;bottom:0;left:0;right:0;background:#b91c1c;color:#fff;padding:10px;font:12px monospace;z-index:99999;white-space:pre-wrap;max-height:40vh;overflow:auto';
  box.textContent = 'window.onerror: ' + e.message + '\n' + (e.error && e.error.stack ? e.error.stack : '');
  document.body.appendChild(box);
});
window.addEventListener('unhandledrejection', (e) => {
  const box = document.createElement('div');
  box.style = 'position:fixed;bottom:0;left:0;right:0;background:#b91c1c;color:#fff;padding:10px;font:12px monospace;z-index:99999;white-space:pre-wrap;max-height:40vh;overflow:auto';
  box.textContent = 'unhandledrejection: ' + (e.reason && e.reason.message ? e.reason.message : String(e.reason)) + '\n' + (e.reason && e.reason.stack ? e.reason.stack : '');
  document.body.appendChild(box);
});

function api(path, options = {}) {
  return fetch('/api' + path, {
    ...options,
    credentials: 'include',
    headers: { 'Content-Type': 'application/json', ...(options.headers || {}) },
  }).then(async (res) => {
    const data = await res.json().catch(() => ({}));
    if (!res.ok) throw new Error(data.error || res.statusText);
    return data;
  });
}

function useToasts() {
  const [toasts, setToasts] = useState([]);
  const push = useCallback((msg, kind) => {
    const id = Math.random().toString(36).slice(2);
    setToasts((t) => [...t, { id, msg, kind: kind || 'ok' }]);
    setTimeout(() => setToasts((t) => t.filter((x) => x.id !== id)), 3200);
  }, []);
  return [toasts, push];
}

function Toasts({ toasts }) {
  return (
    <div className="fixed bottom-4 right-4 z-[999] flex flex-col gap-2">
      {toasts.map((t) => (
        <div key={t.id} className={`rounded-lg px-4 py-2.5 text-sm font-semibold shadow-lg text-white ${t.kind === 'err' ? 'bg-red-600' : 'bg-brand'}`}>
          {t.msg}
        </div>
      ))}
    </div>
  );
}

function Login({ onLoggedIn }) {
  const [password, setPassword] = useState('');
  const [busy, setBusy] = useState(false);
  const [error, setError] = useState('');
  async function submit(e) {
    e.preventDefault();
    setBusy(true);
    setError('');
    try {
      await api('/login', { method: 'POST', body: JSON.stringify({ password }) });
      onLoggedIn();
    } catch (e2) {
      setError('Wrong password');
    } finally {
      setBusy(false);
    }
  }
  return (
    <div className="min-h-screen flex items-center justify-center">
      <form onSubmit={submit} className="clay rounded-2xl bg-white p-8 w-[340px]">
        <div className="flex items-center gap-2 mb-1">
          <div className="w-8 h-8 rounded-lg bg-gradient-to-br from-brand to-brandd grid place-items-center text-white font-bold">G</div>
          <div className="font-bold text-lg">Grocery Mentor</div>
        </div>
        <p className="text-mut text-sm mb-5">Dashboard — enter password</p>
        <input
          type="password"
          autoFocus
          value={password}
          onChange={(e) => setPassword(e.target.value)}
          className="w-full border border-line rounded-lg px-3 py-2.5 mb-3 focus:outline-none focus:ring-2 focus:ring-brand"
          placeholder="Password"
        />
        {error && <p className="text-red-600 text-xs mb-3">{error}</p>}
        <button disabled={busy} className="w-full bg-brand hover:bg-brandd text-white font-bold rounded-lg py-2.5 disabled:opacity-50">
          {busy ? 'Checking…' : 'Enter'}
        </button>
      </form>
    </div>
  );
}

function DeployPill() {
  const [deploy, setDeploy] = useState(null);
  useEffect(() => {
    let stop = false;
    async function poll() {
      try {
        const { deploys } = await api('/deploys');
        if (!stop) setDeploy(deploys[0] || null);
      } catch {}
      if (!stop) setTimeout(poll, 15000);
    }
    poll();
    return () => { stop = true; };
  }, []);
  if (!deploy) return null;
  const status = deploy.status || '';
  const done = status.includes('success');
  const failed = status.includes('failure');
  const color = done ? 'bg-brand' : failed ? 'bg-red-600' : 'bg-amber';
  return (
    <span className={`hidden sm:inline-flex items-center gap-1.5 text-[11px] font-semibold text-white rounded-full px-2.5 py-1 ${color}`}>
      <span className="w-1.5 h-1.5 rounded-full bg-white/80"></span>
      {done ? 'Live' : failed ? 'Deploy failed' : 'Deploying…'}
    </span>
  );
}

function AdminBar({ locale, setLocale, onNew, onLogout, query, setQuery }) {
  return (
    <div className="fixed top-0 left-0 right-0 h-16 z-50 flex items-center justify-between gap-3 px-4 sm:px-6 bg-gradient-to-b from-ab2 to-ab text-white/90 shadow-lg">
      <div className="flex items-center gap-3 min-w-0">
        <div className="w-8 h-8 rounded-lg bg-gradient-to-br from-brand to-brandd grid place-items-center font-bold shrink-0">G</div>
        <div className="font-bold text-sm sm:text-base whitespace-nowrap">Grocery Mentor</div>
        <span className="hidden sm:inline text-[10px] font-bold uppercase tracking-wide bg-brand/20 text-emerald-300 border border-emerald-400/30 px-2 py-0.5 rounded-full">Dashboard</span>
      </div>
      <div className="flex-1 max-w-md hidden md:block">
        <input
          value={query}
          onChange={(e) => setQuery(e.target.value)}
          placeholder="Search guides…"
          className="w-full bg-white/10 border border-white/15 rounded-lg px-3 py-1.5 text-sm placeholder-white/50 focus:outline-none focus:bg-white/15"
        />
      </div>
      <div className="flex items-center gap-2 shrink-0">
        <DeployPill />
        <div className="flex bg-white/10 border border-white/15 rounded-lg p-0.5">
          {['en', 'es'].map((l) => (
            <button key={l} onClick={() => setLocale(l)} className={`px-3 py-1.5 text-xs font-bold rounded-md ${locale === l ? 'bg-brand text-white' : 'text-white/70 hover:bg-white/10'}`}>
              {l.toUpperCase()}
            </button>
          ))}
        </div>
        <button onClick={onNew} className="bg-brand hover:bg-brandd text-white text-xs sm:text-sm font-bold px-3 py-2 rounded-lg">+ New</button>
        <button onClick={onLogout} className="text-white/60 hover:text-white text-xs font-semibold px-2">Logout</button>
      </div>
    </div>
  );
}

function GuideCard({ g, onClick }) {
  return (
    <button onClick={onClick} className="text-left clay rounded-xl bg-white p-4 hover:-translate-y-0.5 transition-transform">
      <div className="flex items-start justify-between gap-2">
        <span className="text-[10px] font-bold uppercase tracking-wide text-brandd bg-brand/10 rounded-full px-2 py-0.5">{g.pillar}</span>
        {g.draft ? <span className="text-[10px] font-bold uppercase text-amber bg-amber/10 rounded-full px-2 py-0.5">Draft</span> : <span className="text-[10px] font-bold uppercase text-brand bg-brand/10 rounded-full px-2 py-0.5">Live</span>}
      </div>
      <div className="font-bold text-sm mt-2 leading-snug">{g.title}</div>
      <div className="text-xs text-mut mt-1 line-clamp-2">{g.description}</div>
      <div className="text-[11px] text-mut/70 mt-2">{g.slug}</div>
    </button>
  );
}

function CharField({ label, value, onChange, max, textarea, placeholder }) {
  const len = (value || '').length;
  const over = max && len > max;
  const Tag = textarea ? 'textarea' : 'input';
  return (
    <div className="mb-4">
      <div className="flex justify-between text-[11px] font-bold uppercase tracking-wide text-mut mb-1">
        <span>{label}</span>
        {max && <span className={over ? 'text-red-600' : ''}>{len}/{max}</span>}
      </div>
      <Tag
        value={value || ''}
        placeholder={placeholder}
        onChange={(e) => onChange(e.target.value)}
        className={`w-full border rounded-lg px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-brand ${over ? 'border-red-400' : 'border-line'} ${textarea ? 'min-h-[90px]' : ''}`}
      />
    </div>
  );
}

function SerpPreview({ title, description, path }) {
  return (
    <div className="border border-line rounded-lg p-3 bg-white">
      <div className="text-[11px] text-mut">grocerymentor.com{path}</div>
      <div className="text-blue-800 text-base leading-snug truncate">{title || 'Untitled'} | Grocery Mentor</div>
      <div className="text-xs text-gray-600 mt-0.5 line-clamp-2">{description}</div>
    </div>
  );
}

function Editor({ locale, slug, onClose, onSaved, onDeleted, toast }) {
  const isNew = slug === '__new__';
  const [loading, setLoading] = useState(!isNew);
  const [saving, setSaving] = useState(false);
  const [tab, setTab] = useState('content');
  const [g, setG] = useState({ title: '', h1: '', description: '', pillar: 'budget', takeaway: '', body: '', draft: true, ogImage: null });
  const [slugField, setSlugField] = useState('');
  const fileRef = useRef(null);

  useEffect(() => {
    if (isNew) return;
    api(`/guides/${locale}/${slug}`).then((data) => setG(data)).catch((e) => toast(e.message, 'err')).finally(() => setLoading(false));
  }, [locale, slug]);

  function set(field, value) { setG((prev) => ({ ...prev, [field]: value })); }

  async function save() {
    setSaving(true);
    try {
      if (isNew) {
        const res = await api(`/guides?locale=${locale}`, { method: 'POST', body: JSON.stringify({ ...g, slug: slugField }) });
        toast('Guide created — deploying now');
        onSaved(res.slug);
      } else {
        await api(`/guides/${locale}/${slug}`, { method: 'PUT', body: JSON.stringify(g) });
        toast('Saved — site is redeploying (1-3 min)');
        onSaved(slug);
      }
    } catch (e) {
      toast(e.message, 'err');
    } finally {
      setSaving(false);
    }
  }

  async function del() {
    if (!confirm(`Delete "${g.title}"? This removes the live page too.`)) return;
    setSaving(true);
    try {
      await api(`/guides/${locale}/${slug}`, { method: 'DELETE' });
      toast('Deleted');
      onDeleted();
    } catch (e) {
      toast(e.message, 'err');
    } finally {
      setSaving(false);
    }
  }

  async function uploadOg(e) {
    const file = e.target.files[0];
    if (!file) return;
    const reader = new FileReader();
    reader.onload = async () => {
      const base64 = reader.result.split(',')[1];
      try {
        const res = await api(`/guides/${locale}/${slug}/og-image`, { method: 'POST', body: JSON.stringify({ data_base64: base64, content_type: file.type }) });
        set('ogImage', res.ogImage);
        toast('OG image uploaded');
      } catch (e2) {
        toast(e2.message, 'err');
      }
    };
    reader.readAsDataURL(file);
  }

  async function removeOg() {
    try {
      await api(`/guides/${locale}/${slug}/og-image`, { method: 'DELETE' });
      set('ogImage', null);
      toast('OG image removed');
    } catch (e) {
      toast(e.message, 'err');
    }
  }

  const path = `/${locale === 'es' ? 'es/' : ''}${g.pillar}/guides/${isNew ? (slugField || 'new-slug') : slug}/`;

  return (
    <div className="fixed inset-0 z-[900] flex">
      <div className="flex-1 bg-black/30" onClick={onClose}></div>
      <div className="w-full max-w-[460px] bg-cream h-full shadow-2xl flex flex-col">
        <div className="flex items-center justify-between px-5 py-4 border-b border-line bg-white">
          <div className="font-bold text-sm">{isNew ? 'New guide' : 'Edit guide'}</div>
          <button onClick={onClose} className="text-mut hover:text-ink text-xl leading-none">&times;</button>
        </div>
        {loading ? (
          <div className="p-6 text-sm text-mut">Loading…</div>
        ) : (
          <React.Fragment>
            <div className="flex gap-1 px-5 pt-3 border-b border-line bg-white">
              {['content', 'seo', 'image'].map((t) => (
                <button key={t} onClick={() => setTab(t)} className={`px-3 py-2 text-xs font-bold rounded-t-md ${tab === t ? 'text-brandd border-b-2 border-brand' : 'text-mut'}`}>
                  {t === 'content' ? 'Content' : t === 'seo' ? 'SEO' : 'OG Image'}
                </button>
              ))}
            </div>
            <div className="flex-1 overflow-y-auto scrollbar-thin p-5">
              {tab === 'content' && (
                <div>
                  {isNew && <CharField label="Slug (url-safe, lowercase-with-dashes)" value={slugField} onChange={setSlugField} placeholder="how-to-save-money" />}
                  <CharField label="Title" value={g.title} onChange={(v) => set('title', v)} />
                  <CharField label="H1 (optional, defaults to title)" value={g.h1} onChange={(v) => set('h1', v)} />
                  <div className="mb-4">
                    <div className="text-[11px] font-bold uppercase tracking-wide text-mut mb-1">Pillar</div>
                    <select value={g.pillar} onChange={(e) => set('pillar', e.target.value)} className="w-full border border-line rounded-lg px-3 py-2 text-sm">
                      {['budget', 'prices-and-value', 'savings', 'meal-planning', 'lists-and-pantry', 'storage-and-waste'].map((p) => (
                        <option key={p} value={p}>{p}</option>
                      ))}
                    </select>
                  </div>
                  <CharField label="Quick answer / takeaway" value={g.takeaway} onChange={(v) => set('takeaway', v)} textarea />
                  <CharField label="Body (Markdown)" value={g.body} onChange={(v) => set('body', v)} textarea placeholder="## Heading&#10;&#10;Paragraph text..." />
                  <div className="flex items-center gap-2 text-sm font-semibold">
                    <input type="checkbox" checked={!g.draft} onChange={(e) => set('draft', !e.target.checked)} className="w-4 h-4 accent-brand" />
                    Published live (uncheck to save as draft, hidden from the site)
                  </div>
                </div>
              )}
              {tab === 'seo' && (
                <div>
                  <CharField label="Meta title" value={g.title} onChange={(v) => set('title', v)} max={60} />
                  <CharField label="Meta description" value={g.description} onChange={(v) => set('description', v)} max={165} textarea />
                  <div className="text-[11px] font-bold uppercase tracking-wide text-mut mb-1">Google preview</div>
                  <SerpPreview title={g.title} description={g.description} path={path} />
                </div>
              )}
              {tab === 'image' && (
                <div>
                  <div className="text-[11px] font-bold uppercase tracking-wide text-mut mb-2">Social share image (og:image)</div>
                  {g.ogImage ? (
                    <div className="rounded-lg overflow-hidden border border-line mb-3">
                      <img src={g.ogImage} alt="OG preview" className="w-full h-[160px] object-cover" />
                    </div>
                  ) : (
                    <div className="rounded-lg border border-dashed border-line h-[160px] grid place-items-center text-mut text-xs mb-3">
                      Using default /og-default.png
                    </div>
                  )}
                  {isNew ? (
                    <p className="text-xs text-mut">Save the guide first, then come back to add a custom OG image.</p>
                  ) : (
                    <div className="flex gap-2">
                      <button onClick={() => fileRef.current.click()} className="bg-brand hover:bg-brandd text-white text-xs font-bold px-3 py-2 rounded-lg">Upload new image</button>
                      {g.ogImage && <button onClick={removeOg} className="text-red-600 text-xs font-bold px-3 py-2 rounded-lg border border-red-200">Remove</button>}
                      <input ref={fileRef} type="file" accept="image/png,image/jpeg,image/webp" className="hidden" onChange={uploadOg} />
                    </div>
                  )}
                  <p className="text-[11px] text-mut mt-2">Recommended: 1200×630px JPG or PNG.</p>
                </div>
              )}
            </div>
            <div className="flex items-center justify-between gap-2 px-5 py-4 border-t border-line bg-white">
              {!isNew ? (
                <button onClick={del} disabled={saving} className="text-red-600 text-xs font-bold px-3 py-2 rounded-lg border border-red-200 disabled:opacity-50">Delete</button>
              ) : <span></span>}
              <button onClick={save} disabled={saving} className="bg-brand hover:bg-brandd text-white text-sm font-bold px-5 py-2.5 rounded-lg disabled:opacity-50">
                {saving ? 'Saving…' : 'Save & Deploy'}
              </button>
            </div>
          </React.Fragment>
        )}
      </div>
    </div>
  );
}

function Dashboard() {
  const [locale, setLocale] = useState('en');
  const [items, setItems] = useState([]);
  const [query, setQuery] = useState('');
  const [openSlug, setOpenSlug] = useState(null);
  const [loading, setLoading] = useState(true);
  const [toasts, toast] = useToasts();

  const load = useCallback(() => {
    setLoading(true);
    api(`/guides?locale=${locale}`).then((d) => setItems(d.items)).catch((e) => toast(e.message, 'err')).finally(() => setLoading(false));
  }, [locale]);

  useEffect(() => { load(); }, [load]);

  const filtered = items.filter((g) => (g.title + ' ' + g.slug + ' ' + g.pillar).toLowerCase().includes(query.toLowerCase()));

  async function logout() {
    await api('/logout', { method: 'POST' });
    window.location.reload();
  }

  return (
    <div>
      <AdminBar locale={locale} setLocale={setLocale} onNew={() => setOpenSlug('__new__')} onLogout={logout} query={query} setQuery={setQuery} />
      <div className="pt-24 px-4 sm:px-8 pb-16 max-w-6xl mx-auto">
        <div className="flex items-center justify-between mb-4">
          <h1 className="font-bold text-xl">Guides — {locale.toUpperCase()} <span className="text-mut font-normal text-sm">({filtered.length})</span></h1>
        </div>
        {loading ? (
          <p className="text-mut text-sm">Loading guides…</p>
        ) : (
          <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4">
            {filtered.map((g) => (
              <GuideCard key={g.slug} g={g} onClick={() => setOpenSlug(g.slug)} />
            ))}
          </div>
        )}
      </div>
      {openSlug && (
        <Editor
          locale={locale}
          slug={openSlug}
          onClose={() => setOpenSlug(null)}
          onSaved={() => { setOpenSlug(null); load(); }}
          onDeleted={() => { setOpenSlug(null); load(); }}
          toast={toast}
        />
      )}
      <Toasts toasts={toasts} />
    </div>
  );
}

function App() {
  const [authed, setAuthed] = useState(null);
  useEffect(() => {
    api('/me').then(() => setAuthed(true)).catch(() => setAuthed(false));
  }, []);
  if (authed === null) return <div className="min-h-screen grid place-items-center text-mut text-sm">Loading…</div>;
  if (!authed) return <Login onLoggedIn={() => setAuthed(true)} />;
  return <Dashboard />;
}

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