---
import Admin from '../../layouts/Admin.astro';
import { getDb } from '../../lib/db';
import { seedByTag } from '../../data/content-seed';
import { STYLE_GROUPS } from '../../lib/style-presets';
export const prerender = false;

// Mappa esplicita prefisso tag → gruppo del pannello (spec §7).
const GROUP_BY_PREFIX: Record<string, string> = {
  home: 'Home',
  about: 'About', team: 'About', method: 'About',
  service: 'Servizi', services: 'Servizi', pricing: 'Servizi',
  training: 'Training',
  contact: 'Contatti', form: 'Contatti',
  global: 'Globali', header: 'Globali', footer: 'Globali',
  agenda: 'Globali', partner: 'Globali', notfound: 'Globali',
};
const GROUP_ORDER = ['Home', 'About', 'Servizi', 'Training', 'Contatti', 'Globali', 'Altro'];

interface Row { tag: string; type: string; value: string; styles: string; updated_at: string; updated_by: string | null }
const rows = getDb().prepare(
  'SELECT tag, type, value, styles, updated_at, updated_by FROM content_blocks ORDER BY tag'
).all() as Row[];

interface Item extends Row { parsedStyles: Record<string, string>; styleOptions: readonly string[] }
const groups = new Map<string, Item[]>(GROUP_ORDER.map((g) => [g, []]));
for (const r of rows) {
  let parsedStyles: Record<string, string> = {};
  try { parsedStyles = JSON.parse(r.styles); } catch { /* styles corrotto: si ignora */ }
  if (typeof parsedStyles !== 'object' || parsedStyles === null) parsedStyles = {};
  const group = GROUP_BY_PREFIX[r.tag.split('.')[0]] ?? 'Altro';
  groups.get(group)!.push({ ...r, parsedStyles, styleOptions: seedByTag.get(r.tag)?.styleOptions ?? [] });
}

const fmt = new Intl.DateTimeFormat('it-IT', { dateStyle: 'short', timeStyle: 'short' });
const snippet = (v: string) => (v.length > 60 ? `${v.slice(0, 60)}…` : v);
const isLong = (v: string) => v.length > 80 || v.includes('\n');
const imgLabel = (v: string) => (v ? '(immagine personalizzata)' : '(immagine originale)');
const allStyles = STYLE_GROUPS as Record<string, readonly string[]>;
---
<Admin title="Contenuti">
  <div class="chead">
    <h1>Contenuti</h1>
    <a href="/?annotate=1" target="_blank">Vedi sito con tag ↗</a>
  </div>
  <input class="afield" id="csearch" type="search" placeholder="Cerca per tag o contenuto…" aria-label="Cerca per tag o contenuto" autocomplete="off" />

  <div id="cgroups">
    {GROUP_ORDER.map((g) => {
      const items = groups.get(g)!;
      return items.length > 0 && (
        <details class="cgroup">
          <summary><strong>{g}</strong> <span class="pill">{items.length}</span></summary>
          {items.map((it) => (
            <details class="crow" id={it.tag} data-tag={it.tag} data-q={`${it.tag} ${it.value}`.toLowerCase()}>
              <summary>
                <code>{it.tag}</code>
                <span class="crow-snippet">{it.type === 'image' ? imgLabel(it.value) : snippet(it.value)}</span>
              </summary>
              <div class="crow-body">
                {it.type === 'image' ? (
                  <>
                    <p class="cimg-preview">
                      {it.value
                        ? <img src={`/uploads/${it.value}`} alt={it.tag} />
                        : <em>Nessun override: il sito usa l'immagine originale.</em>}
                    </p>
                    <p class="cactions">
                      <input type="file" accept="image/*" aria-label={`Nuova immagine per ${it.tag}`} data-file />
                      <button class="abtn" type="button" data-upload>Carica</button>
                      <button class="abtn abtn--danger" type="button" data-restore>Ripristina originale</button>
                    </p>
                  </>
                ) : (
                  <>
                    {isLong(it.value)
                      ? <textarea class="afield" rows="4" aria-label={`Contenuto ${it.tag}`} data-value>{it.value}</textarea>
                      : <input class="afield" value={it.value} aria-label={`Contenuto ${it.tag}`} data-value />}
                    {it.styleOptions.length > 0 && (
                      <p class="cstyles">
                        {it.styleOptions.map((sg) => (
                          <label>{sg}
                            <select data-style={sg}>
                              <option value="">(default)</option>
                              {allStyles[sg].map((v) => (
                                <option value={v} selected={it.parsedStyles[sg] === v}>{v}</option>
                              ))}
                            </select>
                          </label>
                        ))}
                      </p>
                    )}
                    <p class="cactions"><button class="abtn" type="button" data-save>Salva</button></p>
                  </>
                )}
                <p class="form-msg" data-msg hidden></p>
                {it.updated_by && (
                  <p class="cmeta">Aggiornato {fmt.format(new Date(it.updated_at.replace(' ', 'T') + 'Z'))} da {it.updated_by}</p>
                )}
              </div>
            </details>
          ))}
        </details>
      );
    })}
  </div>
  <p id="cempty" hidden>Nessun contenuto corrisponde alla ricerca.</p>
</Admin>

<style is:global>
  .chead { display: flex; justify-content: space-between; align-items: baseline; gap: 16px; flex-wrap: wrap; }
  .chead a { color: #7a6a55; }
  .cgroup { background: #fff; border: 1px solid #ddd; margin-bottom: 14px; }
  .cgroup > summary { padding: 12px 16px; cursor: pointer; background: #eee9e1; font-size: 1.05rem; }
  .crow { border-top: 1px solid #eee; }
  .crow > summary { padding: 8px 16px; cursor: pointer; display: flex; gap: 12px; align-items: baseline; }
  .crow > summary code { background: #f0ece5; padding: 1px 6px; font-size: .82rem; white-space: nowrap; }
  .crow-snippet { color: #888; font-size: .85rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .crow-body { padding: 6px 16px 12px; }
  .crow-body .afield { margin-bottom: 8px; }
  .cstyles { display: flex; gap: 14px; flex-wrap: wrap; margin: 0 0 8px; }
  .cstyles label { font-size: .85rem; color: #555; }
  .cstyles select { margin-left: 4px; padding: 4px; font: inherit; }
  .cactions { margin: 0 0 4px; display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
  .cimg-preview img { max-width: 240px; max-height: 140px; display: block; border: 1px solid #ddd; }
  .cmeta { color: #999; font-size: .78rem; margin: 4px 0 0; }
  .crow--hl { outline: 2px solid #b5a48b; }
</style>

<script>
  const wrap = document.getElementById('cgroups')!;

  function showMsg(row: HTMLElement, ok: boolean, text: string): void {
    const m = row.querySelector<HTMLElement>('[data-msg]')!;
    m.textContent = text;
    m.hidden = false;
    m.className = `form-msg ${ok ? 'form-msg--ok' : 'form-msg--err'}`;
  }

  function setPreview(row: HTMLElement, url: string | null): void {
    const p = row.querySelector<HTMLElement>('.cimg-preview')!;
    if (url) {
      p.textContent = '';
      const img = document.createElement('img');
      img.src = url;
      img.alt = row.dataset.tag ?? '';
      p.append(img);
    } else {
      p.innerHTML = "<em>Nessun override: il sito usa l'immagine originale.</em>";
    }
    row.querySelector<HTMLElement>('.crow-snippet')!.textContent =
      url ? '(immagine personalizzata)' : '(immagine originale)';
  }

  async function call(row: HTMLElement, btn: HTMLButtonElement, req: () => Promise<Response>,
    onOk: (data: Record<string, string>) => void, okMsg: string, errMsg: string): Promise<void> {
    btn.disabled = true;
    try {
      const res = await req();
      const data = await res.json().catch(() => ({}));
      if (res.ok) { onOk(data); showMsg(row, true, okMsg); }
      else showMsg(row, false, data.error ?? errMsg);
    } catch {
      showMsg(row, false, 'Errore di rete.');
    } finally {
      btn.disabled = false;
    }
  }

  wrap.addEventListener('click', (e) => {
    const btn = (e.target as HTMLElement).closest('button');
    if (!btn) return;
    const row = btn.closest<HTMLElement>('details.crow')!;
    const api = `/api/admin/content/${encodeURIComponent(row.dataset.tag!)}`;

    if (btn.hasAttribute('data-save')) {
      const value = row.querySelector<HTMLInputElement | HTMLTextAreaElement>('[data-value]')!.value;
      const styles: Record<string, string> = {};
      row.querySelectorAll<HTMLSelectElement>('[data-style]').forEach((s) => {
        if (s.value) styles[s.dataset.style!] = s.value;
      });
      call(row, btn,
        () => fetch(api, {
          method: 'PUT',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({ value, styles }),
        }),
        (data) => {
          const saved = typeof data.value === 'string' ? data.value : value;
          const field = row.querySelector<HTMLInputElement | HTMLTextAreaElement>('[data-value]')!;
          field.value = saved;
          row.dataset.q = `${row.dataset.tag} ${saved}`.toLowerCase();
          row.querySelector<HTMLElement>('.crow-snippet')!.textContent =
            saved.length > 60 ? `${saved.slice(0, 60)}…` : saved;
        },
        'Salvato.', 'Salvataggio non riuscito.');
    } else if (btn.hasAttribute('data-upload')) {
      const file = row.querySelector<HTMLInputElement>('[data-file]')!.files?.[0];
      if (!file) { showMsg(row, false, 'Seleziona prima un file.'); return; }
      const fd = new FormData();
      fd.append('file', file);
      call(row, btn,
        () => fetch(`${api}/image`, { method: 'POST', body: fd }),
        (data) => setPreview(row, data.url),
        'Immagine caricata.', 'Upload non riuscito.');
    } else if (btn.hasAttribute('data-restore')) {
      call(row, btn,
        () => fetch(`${api}/image`, { method: 'DELETE' }),
        () => setPreview(row, null),
        'Ripristinata immagine originale.', 'Ripristino non riuscito.');
    }
  });

  // Ricerca client-side: nasconde le righe non corrispondenti (tag o contenuto).
  const search = document.getElementById('csearch') as HTMLInputElement;
  const rowEls = Array.from(document.querySelectorAll<HTMLDetailsElement>('details.crow'));
  const groupEls = Array.from(document.querySelectorAll<HTMLDetailsElement>('details.cgroup'));
  search.addEventListener('input', () => {
    const q = search.value.trim().toLowerCase();
    for (const r of rowEls) r.hidden = q !== '' && !(r.dataset.q ?? '').includes(q);
    let any = false;
    for (const g of groupEls) {
      const visible = g.querySelector('details.crow:not([hidden])') !== null;
      g.hidden = !visible;
      if (q && visible) g.open = true;
      any = any || visible;
    }
    document.getElementById('cempty')!.hidden = any;
  });

  // Ancore: #<tag> apre gruppo + riga, scrolla ed evidenzia.
  function openHash(): void {
    let tag: string;
    try { tag = decodeURIComponent(location.hash.slice(1)); } catch { return; }
    if (!tag) return;
    const row = document.getElementById(tag);
    if (!row || !row.classList.contains('crow')) return;
    const group = row.closest<HTMLDetailsElement>('details.cgroup');
    if (group) group.open = true;
    (row as HTMLDetailsElement).open = true;
    row.scrollIntoView({ block: 'center' });
    row.classList.add('crow--hl');
    setTimeout(() => row.classList.remove('crow--hl'), 2500);
  }
  window.addEventListener('hashchange', openHash);
  openHash();
</script>
