// admin/assets/admin.js
(function () {
  const API = '/api/admin';

  function toast(msg, isError) {
    let el = document.querySelector('.toast');
    if (!el) {
      el = document.createElement('div');
      el.className = 'toast';
      document.body.appendChild(el);
    }
    el.textContent = msg;
    el.classList.remove('error', 'success');
    el.classList.add(isError ? 'error' : 'success');
    el.classList.add('show');
    clearTimeout(el._t);
    el._t = setTimeout(() => el.classList.remove('show'), 3400);
  }

  async function api(path, options = {}) {
    const resp = await fetch(path, {
      headers: { 'Content-Type': 'application/json' },
      credentials: 'same-origin',
      ...options
    });
    let data = null;
    try { data = await resp.json(); } catch (e) { /* no body */ }
    if (!resp.ok) {
      const message = (data && data.error) || `Request failed (${resp.status})`;
      const err = new Error(message);
      err.status = resp.status;
      err.data = data;
      throw err;
    }
    return data;
  }

  function escapeHtml(str) {
    return String(str ?? '').replace(/[&<>"']/g, (c) => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' }[c]));
  }
  function formatDate(iso, opts) {
    if (!iso) return '—';
    const d = new Date(String(iso).replace(' ', 'T') + (String(iso).endsWith('Z') ? '' : 'Z'));
    if (isNaN(d)) return iso;
    return d.toLocaleDateString(undefined, opts || { year: 'numeric', month: 'short', day: 'numeric' });
  }
  function formatDateTime(iso) {
    if (!iso) return '—';
    const d = new Date(String(iso).replace(' ', 'T') + (String(iso).endsWith('Z') ? '' : 'Z'));
    if (isNaN(d)) return iso;
    return d.toLocaleString(undefined, { month: 'short', day: 'numeric', hour: 'numeric', minute: '2-digit' });
  }
  function timeAgo(iso) {
    if (!iso) return '—';
    const d = new Date(String(iso).replace(' ', 'T') + (String(iso).endsWith('Z') ? '' : 'Z'));
    if (isNaN(d)) return iso;
    const secs = Math.max(1, Math.round((Date.now() - d.getTime()) / 1000));
    if (secs < 60) return `${secs}s ago`;
    const mins = Math.round(secs / 60);
    if (mins < 60) return `${mins}m ago`;
    const hrs = Math.round(mins / 60);
    if (hrs < 24) return `${hrs}h ago`;
    const days = Math.round(hrs / 24);
    return `${days}d ago`;
  }

  window.SKFAdmin = { api, toast, escapeHtml, formatDate, formatDateTime, timeAgo };

  // =========================================================================
  // NAVIGATION — one source of truth rendered into every page's
  // <nav data-admin-nav></nav>, so adding a page never means editing 8 files.
  // =========================================================================
  const ICONS = {
    dashboard: '<rect x="3" y="3" width="8" height="8" rx="1.5"/><rect x="13" y="3" width="8" height="8" rx="1.5"/><rect x="3" y="13" width="8" height="8" rx="1.5"/><rect x="13" y="13" width="8" height="8" rx="1.5"/>',
    messages: '<rect x="3" y="5" width="18" height="14" rx="2"/><path d="M4 6l8 7 8-7"/>',
    subscribers: '<circle cx="9" cy="8" r="3.5"/><path d="M2.5 20c0-3.6 2.9-6 6.5-6s6.5 2.4 6.5 6"/><circle cx="17" cy="8" r="2.5"/><path d="M15.5 14.2c2.6.4 4.5 2.3 4.5 5.8"/>',
    content: '<path d="M4 5.5C6 4.3 9 4 12 5.2c3-1.2 6-.9 8 .3v13c-2-1.2-5-1.5-8-.3-3-1.2-6-.9-8 .3v-13z"/>',
    media: '<rect x="3" y="4" width="18" height="15" rx="2"/><circle cx="8.5" cy="10" r="1.8"/><path d="M4 17l5-5 3.5 3.5L16 12l4 4.5"/>',
    analytics: '<path d="M4 20V10"/><path d="M11 20V4"/><path d="M18 20v-7"/><path d="M2.5 20.5h19"/>',
    security: '<path d="M12 3l7 3.2v5c0 4.8-2.9 8.2-7 9.8-4.1-1.6-7-5-7-9.8v-5L12 3z"/><path d="M9.3 12l1.8 1.8 3.6-3.8"/>',
    settings: '<circle cx="12" cy="12" r="3"/><path d="M19.4 13a7.9 7.9 0 000-2l2-1.6-2-3.4-2.4 1a8 8 0 00-1.7-1L14.8 3h-4l-.5 2.6a8 8 0 00-1.7 1l-2.4-1-2 3.4L6 11a7.9 7.9 0 000 2l-2 1.6 2 3.4 2.4-1a8 8 0 001.7 1l.5 2.6h4l.5-2.6a8 8 0 001.7-1l2.4 1 2-3.4-2-1.6z"/>',
    invoices: '<path d="M6 3h9l4 4v14a1 1 0 01-1 1H6a1 1 0 01-1-1V4a1 1 0 011-1z"/><path d="M15 3v4h4"/><path d="M8.5 12.5h7M8.5 15.5h7M8.5 9.5h4"/>'
  };

  const NAV_ITEMS = [
    { group: 'Overview', href: 'dashboard.html', label: 'Dashboard', icon: 'dashboard' },
    { group: 'Engagement', href: 'messages.html', label: 'Messages', icon: 'messages', badgeKey: 'newMessages' },
    { group: 'Engagement', href: 'subscribers.html', label: 'Subscribers', icon: 'subscribers' },
    { group: 'Content', href: 'content.html', label: 'Posts', icon: 'content' },
    { group: 'Content', href: 'media.html', label: 'Media', icon: 'media' },
    { group: 'Finance', href: 'invoices.html', label: 'Invoices', icon: 'invoices', badgeKey: 'draftInvoices' },
    { group: 'Insights', href: 'analytics.html', label: 'Analytics', icon: 'analytics' },
    { group: 'Insights', href: 'security.html', label: 'Security', icon: 'security', badgeKey: 'lockedAccounts', badgeDanger: true },
    { group: 'Account', href: 'settings.html', label: 'Settings', icon: 'settings' }
  ];

  function renderNav(stats) {
    const nav = document.querySelector('[data-admin-nav]');
    if (!nav) return;
    const here = location.pathname.split('/').pop() || 'dashboard.html';
    let groups = [];
    NAV_ITEMS.forEach((item) => { if (!groups.includes(item.group)) groups.push(item.group); });
    nav.innerHTML = groups.map((g) => {
      const items = NAV_ITEMS.filter((i) => i.group === g);
      return `<div class="admin-nav-group-label">${g}</div>` + items.map((item) => {
        const badgeVal = stats && item.badgeKey ? stats[item.badgeKey] : 0;
        const badge = badgeVal ? `<span class="nav-dot${item.badgeDanger ? ' danger' : ''}">${badgeVal > 99 ? '99+' : badgeVal}</span>` : '';
        return `<a href="${item.href}" class="${item.href === here ? 'active' : ''}"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8">${ICONS[item.icon]}</svg>${item.label}${badge}</a>`;
      }).join('');
    }).join('');
  }

  function renderSidebarUser(me) {
    const el = document.querySelector('[data-admin-user]');
    if (!el || !me) return;
    const initial = (me.username || '?').charAt(0).toUpperCase();
    el.innerHTML = `
      <div class="avatar">${initial}</div>
      <div class="who">
        <strong>${escapeHtml(me.username)}</strong>
        <span>${escapeHtml(me.role || 'admin')} · last in ${me.lastLoginAt ? timeAgo(me.lastLoginAt) : 'now'}</span>
      </div>`;
  }

  // ---------------- Auth guard (every page except login.html) ----------------
  const isLoginPage = /login\.html$/.test(location.pathname) || location.pathname.endsWith('/admin/') || location.pathname.endsWith('/admin');

  async function guard() {
    try {
      const me = await api(`${API}/me`);
      document.querySelectorAll('[data-admin-username]').forEach((el) => { el.textContent = me.username; });
      renderSidebarUser(me);
      try {
        const stats = await api(`${API}/stats`);
        renderNav(stats);
      } catch (e) { renderNav(null); }
    } catch (e) {
      if (!isLoginPage) location.href = 'login.html';
    }
  }
  if (!isLoginPage) guard(); else renderNav(null);

  // ---------------- Sidebar active link + logout ----------------
  document.addEventListener('DOMContentLoaded', () => {
    const logoutBtn = document.querySelector('[data-logout]');
    if (logoutBtn) {
      logoutBtn.addEventListener('click', async () => {
        try { await api(`${API}/logout`, { method: 'POST' }); } catch (e) { /* ignore */ }
        location.href = 'login.html';
      });
    }

    // -------- LOGIN PAGE --------
    initLoginPage();

    // -------- DASHBOARD PAGE --------
    initDashboard();

    // -------- MESSAGES PAGE --------
    initMessages();

    // -------- SUBSCRIBERS PAGE --------
    initSubscribers();

    // -------- SETTINGS PAGE --------
    initSettings();

    // -------- CONTENT MANAGER PAGE (stories / insights / press / events) --------
    initContentManager();

    // -------- MEDIA MANAGER PAGE --------
    initMediaManager();

    // -------- ANALYTICS PAGE --------
    initAnalytics();

    // -------- SECURITY PAGE --------
    initSecurity();

    // -------- INVOICES PAGE --------
    initInvoices();
  });

  // =========================================================================
  // LOGIN
  // =========================================================================
  function initLoginPage() {
    const loginForm = document.querySelector('#login-form');
    if (!loginForm) return;
    const errBox = document.querySelector('.login-error');
    const submitBtn = loginForm.querySelector('button[type="submit"]');

    const toggle = document.querySelector('[data-pw-toggle]');
    if (toggle) {
      toggle.addEventListener('click', () => {
        const input = document.querySelector('#password');
        const show = input.type === 'password';
        input.type = show ? 'text' : 'password';
        toggle.textContent = show ? 'Hide' : 'Show';
      });
    }

    let lockTimer = null;
    function lockCountdown(seconds) {
      clearInterval(lockTimer);
      let remaining = seconds;
      submitBtn.disabled = true;
      const tick = () => {
        submitBtn.textContent = remaining > 0 ? `Locked — try again in ${remaining}s` : 'Sign In';
        if (remaining <= 0) {
          clearInterval(lockTimer);
          submitBtn.disabled = false;
        }
        remaining -= 1;
      };
      tick();
      lockTimer = setInterval(tick, 1000);
    }

    loginForm.addEventListener('submit', async (e) => {
      e.preventDefault();
      errBox.classList.remove('show');
      const fd = new FormData(loginForm);
      try {
        await api(`${API}/login`, { method: 'POST', body: JSON.stringify({ username: fd.get('username'), password: fd.get('password') }) });
        location.href = 'dashboard.html';
      } catch (err) {
        errBox.textContent = err.message;
        errBox.classList.add('show');
        if (err.status === 423 && err.data && err.data.lockedForSeconds) {
          lockCountdown(err.data.lockedForSeconds);
        }
      }
    });
  }

  // =========================================================================
  // DASHBOARD
  // =========================================================================
  function initDashboard() {
    const statCards = document.querySelector('[data-stat-cards]');
    if (!statCards) return;

    api(`${API}/stats`).then((s) => {
      const cards = [
        { n: s.newMessages, l: 'New Messages', icon: 'messages', tone: 'navy' },
        { n: s.subscribers, l: 'Newsletter Subscribers', icon: 'subscribers', tone: '' },
        { n: s.upcomingEvents, l: 'Upcoming Events', icon: 'dashboard', tone: 'gold' },
        { n: s.pageViewsToday, l: 'Page Views Today', icon: 'analytics', tone: 'navy' },
        { n: s.stories, l: 'Projects', icon: 'content', tone: '' },
        { n: s.insights, l: 'Insight Posts', icon: 'content', tone: '' },
        { n: s.media, l: 'Media Items', icon: 'media', tone: 'gold' },
        { n: s.lockedAccounts, l: 'Locked Accounts', icon: 'security', tone: s.lockedAccounts ? 'danger' : '' }
      ];
      statCards.innerHTML = cards.map((c) => `
        <div class="stat-card">
          <div class="icon ${c.tone}"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8">${ICONS[c.icon]}</svg></div>
          <div><span class="n">${c.n ?? 0}</span><span class="l">${c.l}</span></div>
        </div>`).join('');
    }).catch((err) => toast(err.message, true));

    const feed = document.querySelector('[data-activity-feed]');
    if (feed) {
      api(`${API}/audit-log?limit=8`).then((rows) => {
        if (!rows.length) { feed.innerHTML = '<p class="empty-state">No activity yet.</p>'; return; }
        feed.innerHTML = rows.map((r) => activityRowHTML(r)).join('');
      }).catch(() => { feed.innerHTML = '<p class="empty-state">Could not load recent activity.</p>'; });
    }

    const miniChart = document.querySelector('[data-mini-chart]');
    if (miniChart) {
      api('/api/admin/analytics/summary').then((data) => {
        miniChart.innerHTML = lineChartSVG(data.dailySeries, 620, 130);
      }).catch(() => { miniChart.innerHTML = '<p class="empty-state">No traffic data yet.</p>'; });
    }
  }

  function activityRowHTML(r) {
    const dotClass = r.action === 'delete' ? 'danger' : (r.action === 'login_failed' ? 'warn' : '');
    const verb = {
      create: 'created', update: 'updated', delete: 'deleted', publish: 'published', unpublish: 'unpublished',
      login: 'signed in', login_failed: 'failed to sign in', logout: 'signed out', unlock: 'unlocked an account'
    }[r.action] || r.action;
    return `<div class="activity-item">
      <span class="dot ${dotClass}"></span>
      <div class="meta">
        <div class="line1"><b>${escapeHtml(r.admin_username)}</b> ${verb}${r.entity && r.entity !== 'session' ? ` <em>${escapeHtml(r.entity)}</em>` : ''}${r.details ? ` — ${escapeHtml(r.details)}` : ''}</div>
        <div class="line2">${timeAgo(r.created_at)} · ${escapeHtml(r.ip || '')}</div>
      </div>
    </div>`;
  }

  // =========================================================================
  // MESSAGES
  // =========================================================================
  function initMessages() {
    const messagesBody = document.querySelector('#messages-body');
    if (!messagesBody) return;

    function renderMessages(rows) {
      if (!rows.length) {
        messagesBody.innerHTML = '';
        document.querySelector('#messages-empty').style.display = 'block';
        return;
      }
      document.querySelector('#messages-empty').style.display = 'none';
      messagesBody.innerHTML = rows.map((m) => `
        <tr data-id="${m.id}">
          <td><span class="badge ${m.status === 'new' ? 'badge-new' : 'badge-read'}">${m.status}</span></td>
          <td><strong>${escapeHtml(m.name)}</strong><br><span style="color:var(--ink-soft);font-size:.85rem;">${escapeHtml(m.email)}</span></td>
          <td>${escapeHtml(m.reason || '—')}</td>
          <td style="max-width:320px;">${escapeHtml(m.message)}</td>
          <td style="white-space:nowrap;">${formatDate(m.created_at)}</td>
          <td>
            <div class="row-actions">
              <button class="icon-btn" data-toggle-status>${m.status === 'new' ? 'Mark read' : 'Mark new'}</button>
              <button class="icon-btn danger" data-delete>Delete</button>
            </div>
          </td>
        </tr>
      `).join('');
    }

    function loadMessages() {
      api('/api/contact').then(renderMessages).catch((err) => toast(err.message, true));
    }
    loadMessages();

    messagesBody.addEventListener('click', async (e) => {
      const tr = e.target.closest('tr');
      if (!tr) return;
      const id = tr.getAttribute('data-id');
      try {
        if (e.target.matches('[data-toggle-status]')) {
          const isNew = tr.querySelector('.badge').textContent.trim() === 'new';
          await api(`/api/contact/${id}`, { method: 'PATCH', body: JSON.stringify({ status: isNew ? 'read' : 'new' }) });
          loadMessages();
        }
        if (e.target.matches('[data-delete]')) {
          if (!confirm('Delete this message? This cannot be undone.')) return;
          await api(`/api/contact/${id}`, { method: 'DELETE' });
          toast('Message deleted.');
          loadMessages();
        }
      } catch (err) { toast(err.message, true); }
    });
  }

  // =========================================================================
  // SUBSCRIBERS
  // =========================================================================
  function initSubscribers() {
    const subsBody = document.querySelector('#subscribers-body');
    if (!subsBody) return;

    function renderSubs(rows) {
      if (!rows.length) {
        subsBody.innerHTML = '';
        document.querySelector('#subscribers-empty').style.display = 'block';
        return;
      }
      document.querySelector('#subscribers-empty').style.display = 'none';
      subsBody.innerHTML = rows.map((s) => `
        <tr data-id="${s.id}">
          <td>${escapeHtml(s.email)}</td>
          <td>${formatDate(s.created_at)}</td>
          <td><button class="icon-btn danger" data-delete>Remove</button></td>
        </tr>
      `).join('');
    }
    function loadSubs() { api('/api/newsletter').then(renderSubs).catch((err) => toast(err.message, true)); }
    loadSubs();
    subsBody.addEventListener('click', async (e) => {
      if (!e.target.matches('[data-delete]')) return;
      const tr = e.target.closest('tr');
      if (!confirm('Remove this subscriber? This cannot be undone.')) return;
      try {
        await api(`/api/newsletter/${tr.getAttribute('data-id')}`, { method: 'DELETE' });
        toast('Subscriber removed.');
        loadSubs();
      } catch (err) { toast(err.message, true); }
    });
    const exportBtn = document.querySelector('#export-subscribers');
    if (exportBtn) {
      exportBtn.addEventListener('click', async () => {
        try {
          const rows = await api('/api/newsletter');
          const csv = 'email,subscribed_at\n' + rows.map((r) => `${r.email},${r.created_at}`).join('\n');
          const blob = new Blob([csv], { type: 'text/csv' });
          const url = URL.createObjectURL(blob);
          const a = document.createElement('a');
          a.href = url; a.download = 'subscribers.csv'; a.click();
          URL.revokeObjectURL(url);
        } catch (err) { toast(err.message, true); }
      });
    }
  }

  // =========================================================================
  // SETTINGS (change password with strength meter)
  // =========================================================================
  function initSettings() {
    const pwForm = document.querySelector('#password-form');
    if (!pwForm) return;

    const newPwInput = pwForm.querySelector('#newPassword');
    const meterBar = document.querySelector('[data-pw-meter] i');
    const meterLabel = document.querySelector('[data-pw-meter-label]');
    if (newPwInput && meterBar) {
      newPwInput.addEventListener('input', () => {
        const { score, label, color } = scorePassword(newPwInput.value);
        meterBar.style.width = `${score}%`;
        meterBar.style.background = color;
        if (meterLabel) meterLabel.textContent = newPwInput.value ? label : '';
      });
    }

    pwForm.addEventListener('submit', async (e) => {
      e.preventDefault();
      const fd = new FormData(pwForm);
      const newPassword = fd.get('newPassword');
      if (newPassword !== fd.get('confirmPassword')) {
        toast('New passwords do not match.', true);
        return;
      }
      try {
        await api(`${API}/change-password`, {
          method: 'POST',
          body: JSON.stringify({ currentPassword: fd.get('currentPassword'), newPassword })
        });
        toast('Password updated.');
        pwForm.reset();
        if (meterBar) meterBar.style.width = '0%';
      } catch (err) { toast(err.message, true); }
    });
  }

  function scorePassword(value) {
    if (!value) return { score: 0, label: '', color: 'var(--admin-danger)' };
    let score = Math.min(50, value.length * 5);
    const classes = [/[a-z]/, /[A-Z]/, /[0-9]/, /[^a-zA-Z0-9]/].filter((re) => re.test(value)).length;
    score += classes * 12.5;
    score = Math.min(100, Math.round(score));
    let label = 'Weak', color = 'var(--admin-danger)';
    if (score >= 80) { label = 'Strong'; color = 'var(--admin-success)'; }
    else if (score >= 55) { label = 'Good'; color = 'var(--gold-deep)'; }
    else if (score >= 30) { label = 'Fair'; color = 'var(--admin-warn)'; }
    return { score, label, color };
  }

  // =========================================================================
  // CONTENT MANAGER — stories / insights / press / events (the site's "posts")
  // =========================================================================
  function initContentManager() {
    const root = document.querySelector('[data-content-manager]');
    if (!root) return;

    const resources = {
      stories: {
        label: 'Projects',
        fields: [
          { name: 'category', label: 'Category', type: 'select', options: ['roads', 'government', 'buildings', 'mining'] },
          { name: 'tone', label: 'Card Colour', type: 'select', options: ['tone-navy', 'tone-navy2', 'tone-sage', 'tone-sagelight', 'tone-gold'] },
          { name: 'title', label: 'Title', type: 'text' },
          { name: 'excerpt', label: 'Excerpt', type: 'textarea' },
          { name: 'body', label: 'Full Story (optional)', type: 'textarea' },
          { name: 'published', label: 'Published', type: 'select', options: ['1', '0'] }
        ]
      },
      insights: {
        label: 'Insights (Posts)',
        fields: [
          { name: 'tab', label: 'Section', type: 'select', options: ['publications', 'newsletters'] },
          { name: 'tone', label: 'Card Colour', type: 'select', options: ['tone-navy', 'tone-navy2', 'tone-sage', 'tone-sagelight', 'tone-gold'] },
          { name: 'badge', label: 'Badge Text', type: 'text' },
          { name: 'title', label: 'Title', type: 'text' },
          { name: 'excerpt', label: 'Excerpt', type: 'textarea' },
          { name: 'link', label: 'Link URL', type: 'text' },
          { name: 'published', label: 'Published', type: 'select', options: ['1', '0'] }
        ]
      },
      press: {
        label: 'Media & Press',
        fields: [
          { name: 'tone', label: 'Card Colour', type: 'select', options: ['tone-navy', 'tone-navy2', 'tone-sage', 'tone-sagelight', 'tone-gold'] },
          { name: 'badge', label: 'Badge Text', type: 'text' },
          { name: 'title', label: 'Title', type: 'text' },
          { name: 'excerpt', label: 'Excerpt', type: 'textarea' },
          { name: 'link', label: 'Link URL', type: 'text' },
          { name: 'published', label: 'Published', type: 'select', options: ['1', '0'] }
        ]
      },
      events: {
        label: 'Events',
        fields: [
          { name: 'title', label: 'Title', type: 'text' },
          { name: 'description', label: 'Description', type: 'textarea' },
          { name: 'event_date', label: 'Date', type: 'date' },
          { name: 'published', label: 'Published', type: 'select', options: ['1', '0'] }
        ]
      }
    };

    let active = 'stories';
    const tabRow = root.querySelector('.admin-tab-row');
    const listEl = root.querySelector('[data-content-list]');
    const formPanel = root.querySelector('[data-content-form-panel]');
    const formEl = root.querySelector('[data-content-form]');
    const addBtn = root.querySelector('[data-add-item]');
    const cancelBtn = root.querySelector('[data-cancel-form]');

    tabRow.innerHTML = Object.keys(resources).map((key, i) =>
      `<button class="admin-tab-btn ${i === 0 ? 'active' : ''}" data-key="${key}">${resources[key].label}</button>`
    ).join('');

    tabRow.addEventListener('click', (e) => {
      const btn = e.target.closest('.admin-tab-btn');
      if (!btn) return;
      active = btn.getAttribute('data-key');
      tabRow.querySelectorAll('.admin-tab-btn').forEach((b) => b.classList.toggle('active', b === btn));
      hideForm();
      loadList();
    });

    addBtn.addEventListener('click', () => showForm(null));
    cancelBtn.addEventListener('click', hideForm);

    function hideForm() { formPanel.style.display = 'none'; formEl.reset(); formEl.removeAttribute('data-edit-id'); }

    function showForm(item) {
      const cfg = resources[active];
      formEl.innerHTML = cfg.fields.map((f) => {
        const value = item ? (item[f.name] ?? '') : (f.name === 'published' ? '1' : '');
        if (f.type === 'select') {
          return `<div><label>${f.label}</label><select name="${f.name}">${f.options.map((o) => `<option value="${o}" ${String(value) === o ? 'selected' : ''}>${o}</option>`).join('')}</select></div>`;
        }
        if (f.type === 'textarea') {
          return `<div><label>${f.label}</label><textarea name="${f.name}">${escapeHtml(value)}</textarea></div>`;
        }
        return `<div><label>${f.label}</label><input type="${f.type}" name="${f.name}" value="${escapeHtml(value)}"></div>`;
      }).join('') + `<div class="row-actions" style="margin-top:6px;">
          <button type="submit" class="btn btn-primary">${item ? 'Save changes' : 'Publish new item'}</button>
        </div>`;
      if (item) formEl.setAttribute('data-edit-id', item.id); else formEl.removeAttribute('data-edit-id');
      formPanel.style.display = 'block';
      formPanel.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
    }

    formEl.addEventListener('submit', async (e) => {
      e.preventDefault();
      const fd = new FormData(formEl);
      const payload = {};
      resources[active].fields.forEach((f) => { payload[f.name] = fd.get(f.name); });
      const editId = formEl.getAttribute('data-edit-id');
      try {
        if (editId) {
          await api(`/api/admin/${active}/${editId}`, { method: 'PUT', body: JSON.stringify(payload) });
          toast('Changes saved.');
        } else {
          await api(`/api/admin/${active}`, { method: 'POST', body: JSON.stringify(payload) });
          toast(payload.published == 1 ? 'Published.' : 'Saved as draft.');
        }
        hideForm();
        loadList();
      } catch (err) { toast(err.message, true); }
    });

    function loadList() {
      api(`/api/admin/${active}`).then((rows) => {
        if (!rows.length) {
          listEl.innerHTML = '<p class="empty-state">Nothing here yet. Click "Add item" to create the first entry.</p>';
          return;
        }
        listEl.innerHTML = `<table class="admin-table"><thead><tr>
            <th>Title</th><th>Details</th><th>Status</th><th></th>
          </tr></thead><tbody>${rows.map((r) => `
            <tr data-id="${r.id}">
              <td><strong>${escapeHtml(r.title)}</strong></td>
              <td style="max-width:340px;color:var(--ink-soft);">${escapeHtml(r.excerpt || r.description || '')}</td>
              <td><span class="badge ${r.published == 1 ? 'badge-read' : 'badge-new'}">${r.published == 1 ? 'published' : 'draft'}</span></td>
              <td>
                <div class="row-actions">
                  <button class="icon-btn" data-edit>Edit</button>
                  <button class="icon-btn danger" data-delete data-confirm-delete>Delete</button>
                </div>
              </td>
            </tr>`).join('')}</tbody></table>`;
      }).catch((err) => toast(err.message, true));
    }

    listEl.addEventListener('click', async (e) => {
      const tr = e.target.closest('tr');
      if (!tr) return;
      const id = tr.getAttribute('data-id');
      if (e.target.matches('[data-edit]')) {
        const rows = await api(`/api/admin/${active}`);
        const item = rows.find((r) => String(r.id) === id);
        showForm(item);
      }
      if (e.target.matches('[data-delete]')) {
        if (!confirm('Delete this item? It will be removed from the public site immediately and cannot be undone.')) return;
        try {
          await api(`/api/admin/${active}/${id}`, { method: 'DELETE' });
          toast('Deleted.');
          loadList();
        } catch (err) { toast(err.message, true); }
      }
    });

    loadList();
  }

  // =========================================================================
  // MEDIA MANAGER — photo/video gallery with upload
  // =========================================================================
  function initMediaManager() {
    const root = document.querySelector('[data-media-manager]');
    if (!root) return;

    const grid = root.querySelector('[data-media-grid]');
    const formPanel = root.querySelector('[data-media-form-panel]');
    const formEl = root.querySelector('[data-media-form]');
    const addBtn = root.querySelector('[data-add-media]');
    const cancelBtn = root.querySelector('[data-cancel-media-form]');
    const dropzone = root.querySelector('[data-media-dropzone]');
    const fileInput = root.querySelector('[data-media-file]');
    const filterRow = root.querySelector('[data-media-filter]');
    const urlPreview = root.querySelector('[data-media-url-preview]');

    let currentUrl = '';
    let filter = 'all';

    function hideForm() { formPanel.style.display = 'none'; formEl.reset(); formEl.removeAttribute('data-edit-id'); currentUrl = ''; renderPreview(); }

    function renderPreview() {
      if (!urlPreview) return;
      urlPreview.innerHTML = currentUrl
        ? `<img src="${escapeHtml(currentUrl)}" alt="" style="max-width:180px;border-radius:10px;display:block;">`
        : '<span class="field-hint">No file uploaded yet — drop an image below, or paste a URL.</span>';
    }

    function showForm(item) {
      currentUrl = item ? item.url : '';
      formEl.innerHTML = `
        <div class="row2">
          <div><label>Type</label><select name="media_type">
            <option value="image" ${!item || item.media_type === 'image' ? 'selected' : ''}>Image</option>
            <option value="video" ${item && item.media_type === 'video' ? 'selected' : ''}>Video</option>
          </select></div>
          <div><label>Category</label><select name="category">
            <option value="gallery" ${!item || item.category === 'gallery' ? 'selected' : ''}>Gallery</option>
            <option value="event" ${item && item.category === 'event' ? 'selected' : ''}>Event</option>
            <option value="press" ${item && item.category === 'press' ? 'selected' : ''}>Press</option>
            <option value="banner" ${item && item.category === 'banner' ? 'selected' : ''}>Banner</option>
          </select></div>
        </div>
        <div><label>Title</label><input type="text" name="title" value="${escapeHtml(item ? item.title : '')}" required></div>
        <div><label>Caption (optional)</label><textarea name="caption">${escapeHtml(item ? item.caption || '' : '')}</textarea></div>
        <div><label>File URL</label><input type="text" name="url" value="${escapeHtml(item ? item.url || '' : '')}" placeholder="Upload below, or paste an external image/video URL" data-url-field></div>
        <div data-media-url-preview></div>
        <div><label>Published</label><select name="published">
          <option value="1" ${!item || item.published == 1 ? 'selected' : ''}>Published</option>
          <option value="0" ${item && item.published == 0 ? 'selected' : ''}>Draft</option>
        </select></div>
        <div class="row-actions" style="margin-top:6px;"><button type="submit" class="btn btn-primary">${item ? 'Save changes' : 'Add to Media'}</button></div>
      `;
      const urlField = formEl.querySelector('[data-url-field]');
      urlField.addEventListener('input', () => { currentUrl = urlField.value; renderPreview(); });
      if (item) formEl.setAttribute('data-edit-id', item.id); else formEl.removeAttribute('data-edit-id');
      formPanel.style.display = 'block';
      renderPreview();
      formPanel.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
    }

    addBtn.addEventListener('click', () => showForm(null));
    cancelBtn.addEventListener('click', hideForm);

    // ---- upload handling (drag/drop + click) ----
    function fileToDataUrl(file) {
      return new Promise((resolve, reject) => {
        const reader = new FileReader();
        reader.onload = () => resolve(reader.result);
        reader.onerror = reject;
        reader.readAsDataURL(file);
      });
    }

    async function handleFiles(files) {
      const file = files[0];
      if (!file) return;
      if (!/^image\/(jpeg|png|webp|gif)$/.test(file.type)) {
        toast('Only JPG, PNG, WEBP or GIF images can be uploaded.', true);
        return;
      }
      if (file.size > 4 * 1024 * 1024) {
        toast('Please keep uploads under 4MB.', true);
        return;
      }
      if (formPanel.style.display === 'none') showForm(null);
      dropzone.classList.add('dragover');
      dropzone.querySelector('strong').textContent = 'Uploading…';
      try {
        const dataUrl = await fileToDataUrl(file);
        const result = await api(`${API}/media/upload`, { method: 'POST', body: JSON.stringify({ dataUrl, filename: file.name }) });
        currentUrl = result.url;
        const urlField = formEl.querySelector('[data-url-field]');
        if (urlField) urlField.value = currentUrl;
        const titleField = formEl.querySelector('[name="title"]');
        if (titleField && !titleField.value) titleField.value = file.name.replace(/\.[^.]+$/, '').replace(/[-_]/g, ' ');
        renderPreview();
        toast('Image uploaded.');
      } catch (err) {
        toast(err.message, true);
      } finally {
        dropzone.classList.remove('dragover');
        dropzone.querySelector('strong').textContent = 'Click to upload, or drag an image here';
      }
    }

    if (dropzone) {
      dropzone.addEventListener('click', () => fileInput.click());
      dropzone.addEventListener('dragover', (e) => { e.preventDefault(); dropzone.classList.add('dragover'); });
      dropzone.addEventListener('dragleave', () => dropzone.classList.remove('dragover'));
      dropzone.addEventListener('drop', (e) => { e.preventDefault(); dropzone.classList.remove('dragover'); handleFiles(e.dataTransfer.files); });
    }
    if (fileInput) fileInput.addEventListener('change', () => handleFiles(fileInput.files));

    formEl.addEventListener('submit', async (e) => {
      e.preventDefault();
      const fd = new FormData(formEl);
      const payload = {
        media_type: fd.get('media_type'), category: fd.get('category'), title: fd.get('title'),
        caption: fd.get('caption'), url: fd.get('url') || currentUrl, thumbnail_url: null, published: fd.get('published')
      };
      if (!payload.url) { toast('Please upload a file or provide a URL first.', true); return; }
      const editId = formEl.getAttribute('data-edit-id');
      try {
        if (editId) {
          await api(`${API}/media/${editId}`, { method: 'PUT', body: JSON.stringify(payload) });
          toast('Media item updated.');
        } else {
          await api(`${API}/media`, { method: 'POST', body: JSON.stringify(payload) });
          toast(payload.published == 1 ? 'Published to the Media page.' : 'Saved as draft.');
        }
        hideForm();
        loadGrid();
      } catch (err) { toast(err.message, true); }
    });

    if (filterRow) {
      filterRow.addEventListener('click', (e) => {
        const btn = e.target.closest('.admin-tab-btn');
        if (!btn) return;
        filter = btn.getAttribute('data-key');
        filterRow.querySelectorAll('.admin-tab-btn').forEach((b) => b.classList.toggle('active', b === btn));
        loadGrid();
      });
    }

    function mediaTileHTML(item) {
      const thumb = item.url
        ? (item.media_type === 'video'
            ? `<video src="${escapeHtml(item.url)}" muted></video>`
            : `<img src="${escapeHtml(item.url)}" alt="${escapeHtml(item.title)}">`)
        : `<div class="placeholder"><svg viewBox="0 0 24 24" width="30" height="30" fill="none" stroke="currentColor" stroke-width="1.6"><rect x="3" y="4" width="18" height="15" rx="2"/><circle cx="8.5" cy="10" r="1.8"/><path d="M4 17l5-5 3.5 3.5L16 12l4 4.5"/></svg></div>`;
      return `<div class="media-tile" data-id="${item.id}">
        <div class="thumb">${thumb}<span class="badge ${item.published == 1 ? 'badge-read' : 'badge-new'}">${item.published == 1 ? 'live' : 'draft'}</span></div>
        <div class="body">
          <h4>${escapeHtml(item.title)}</h4>
          <p>${escapeHtml(item.caption || item.category)}</p>
          <div class="row-actions">
            <button class="icon-btn" data-edit-media>Edit</button>
            <button class="icon-btn danger" data-delete-media>Delete</button>
          </div>
        </div>
      </div>`;
    }

    function loadGrid() {
      api(`${API}/media`).then((rows) => {
        const filtered = filter === 'all' ? rows : rows.filter((r) => r.category === filter);
        if (!filtered.length) {
          grid.innerHTML = '<p class="empty-state">No media items in this category yet. Upload a photo to get started.</p>';
          return;
        }
        grid.innerHTML = filtered.map(mediaTileHTML).join('');
      }).catch((err) => toast(err.message, true));
    }

    grid.addEventListener('click', async (e) => {
      const tile = e.target.closest('.media-tile');
      if (!tile) return;
      const id = tile.getAttribute('data-id');
      if (e.target.matches('[data-edit-media]')) {
        const rows = await api(`${API}/media`);
        const item = rows.find((r) => String(r.id) === id);
        showForm(item);
      }
      if (e.target.matches('[data-delete-media]')) {
        if (!confirm('Delete this media item? It will disappear from the public Media page immediately.')) return;
        try {
          await api(`${API}/media/${id}`, { method: 'DELETE' });
          toast('Media item deleted.');
          loadGrid();
        } catch (err) { toast(err.message, true); }
      }
    });

    loadGrid();
  }

  // =========================================================================
  // ANALYTICS
  // =========================================================================
  function lineChartSVG(series, width, height) {
    series = series || [];
    const pad = { top: 14, right: 14, bottom: 24, left: 14 };
    const w = width - pad.left - pad.right;
    const h = height - pad.top - pad.bottom;
    const max = Math.max(1, ...series.map((d) => d.views));
    const stepX = series.length > 1 ? w / (series.length - 1) : 0;
    const points = series.map((d, i) => {
      const x = pad.left + i * stepX;
      const y = pad.top + h - (d.views / max) * h;
      return [x, y];
    });
    const linePath = points.map((p, i) => `${i === 0 ? 'M' : 'L'}${p[0].toFixed(1)},${p[1].toFixed(1)}`).join(' ');
    const areaPath = points.length
      ? `${linePath} L${points[points.length - 1][0].toFixed(1)},${(pad.top + h).toFixed(1)} L${points[0][0].toFixed(1)},${(pad.top + h).toFixed(1)} Z`
      : '';
    const dots = points.map((p, i) => `<circle cx="${p[0].toFixed(1)}" cy="${p[1].toFixed(1)}" r="3.2" fill="var(--sage-deep)"><title>${escapeHtml(series[i].day)}: ${series[i].views} views</title></circle>`).join('');
    const labels = series.map((d, i) => {
      if (series.length > 8 && i % 2 !== 0) return '';
      const x = pad.left + i * stepX;
      return `<text x="${x.toFixed(1)}" y="${height - 6}" font-size="9" fill="var(--ink-soft)" text-anchor="middle">${escapeHtml((d.day || '').slice(5))}</text>`;
    }).join('');
    if (!series.length) return '<p class="empty-state">No traffic recorded yet — once the site gets visits, they will show up here.</p>';
    return `<div class="chart-wrap"><svg viewBox="0 0 ${width} ${height}" preserveAspectRatio="none">
      <defs><linearGradient id="areaFill" x1="0" y1="0" x2="0" y2="1">
        <stop offset="0%" stop-color="var(--sage)" stop-opacity="0.35"/>
        <stop offset="100%" stop-color="var(--sage)" stop-opacity="0"/>
      </linearGradient></defs>
      <path d="${areaPath}" fill="url(#areaFill)" stroke="none"/>
      <path d="${linePath}" fill="none" stroke="var(--sage-deep)" stroke-width="2.2" stroke-linejoin="round" stroke-linecap="round"/>
      ${dots}${labels}
    </svg></div>`;
  }

  function barListHTML(rows, labelKey, valueKey) {
    if (!rows.length) return '<p class="empty-state">Nothing recorded in the last 30 days.</p>';
    const max = Math.max(1, ...rows.map((r) => r[valueKey]));
    return `<div class="bar-list">${rows.map((r) => `
      <div class="bar-row">
        <span class="label">${escapeHtml(r[labelKey])}</span>
        <span class="count">${r[valueKey]}</span>
        <div class="bar-track"><i style="width:${Math.max(4, (r[valueKey] / max) * 100)}%"></i></div>
      </div>`).join('')}</div>`;
  }

  function initAnalytics() {
    const root = document.querySelector('[data-analytics-page]');
    if (!root) return;

    api('/api/admin/analytics/summary').then((data) => {
      root.querySelector('[data-analytics-totals]').innerHTML = `
        <div class="stat-card"><div class="icon navy"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8">${ICONS.analytics}</svg></div><div><span class="n">${data.totalViews}</span><span class="l">Total Page Views</span></div></div>
        <div class="stat-card"><div class="icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8">${ICONS.dashboard}</svg></div><div><span class="n">${data.viewsToday}</span><span class="l">Views Today</span></div></div>
        <div class="stat-card"><div class="icon gold"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8">${ICONS.subscribers}</svg></div><div><span class="n">${data.uniqueVisitors30d}</span><span class="l">Unique Visitors (30d)</span></div></div>
      `;
      root.querySelector('[data-analytics-chart]').innerHTML = lineChartSVG(data.dailySeries, 900, 220);
      root.querySelector('[data-top-pages]').innerHTML = barListHTML(data.topPages, 'path', 'views');
      root.querySelector('[data-top-referrers]').innerHTML = barListHTML(data.topReferrers, 'referrer', 'visits');

      const deviceEl = root.querySelector('[data-device-breakdown]');
      if (deviceEl) {
        const total = data.deviceBreakdown.reduce((sum, d) => sum + d.n, 0) || 1;
        deviceEl.innerHTML = data.deviceBreakdown.length
          ? data.deviceBreakdown.map((d) => `<span>${Math.round((d.n / total) * 100)}% ${escapeHtml(d.device)}</span>`).join(' · ')
          : 'No data yet.';
      }
    }).catch((err) => toast(err.message, true));
  }

  // =========================================================================
  // SECURITY — audit log, login attempts, account lockouts ("punishment")
  // =========================================================================
  function initSecurity() {
    const root = document.querySelector('[data-security-page]');
    if (!root) return;

    function loadAccounts() {
      api(`${API}/security/accounts`).then((rows) => {
        const el = root.querySelector('[data-security-accounts]');
        el.innerHTML = rows.map((u) => `
          <tr data-id="${u.id}">
            <td><strong>${escapeHtml(u.username)}</strong></td>
            <td>${escapeHtml(u.role)}</td>
            <td>${u.failed_attempts || 0}</td>
            <td>${u.isLocked ? `<span class="security-pill locked">Locked until ${formatDateTime(u.locked_until)}</span>` : '<span class="security-pill ok">OK</span>'}</td>
            <td>${u.last_login_at ? `${timeAgo(u.last_login_at)} · ${escapeHtml(u.last_login_ip || '')}` : 'Never'}</td>
            <td>${u.isLocked ? '<button class="icon-btn" data-unlock>Unlock now</button>' : ''}</td>
          </tr>`).join('');
      }).catch((err) => toast(err.message, true));
    }
    loadAccounts();

    root.addEventListener('click', async (e) => {
      if (!e.target.matches('[data-unlock]')) return;
      const tr = e.target.closest('tr');
      const id = tr.getAttribute('data-id');
      try {
        await api(`${API}/security/unlock/${id}`, { method: 'POST' });
        toast('Account unlocked.');
        loadAccounts();
      } catch (err) { toast(err.message, true); }
    });

    api(`${API}/security/login-attempts?limit=25`).then((rows) => {
      const el = root.querySelector('[data-login-attempts]');
      if (!el) return;
      if (!rows.length) { el.innerHTML = '<p class="empty-state">No login attempts recorded yet.</p>'; return; }
      el.innerHTML = `<table class="admin-table"><thead><tr><th>Result</th><th>Username</th><th>IP</th><th>When</th></tr></thead><tbody>
        ${rows.map((r) => `<tr>
          <td><span class="badge ${r.success ? 'badge-success' : 'badge-danger'}">${r.success ? 'success' : 'failed'}</span></td>
          <td>${escapeHtml(r.username)}</td>
          <td>${escapeHtml(r.ip || '—')}</td>
          <td>${timeAgo(r.created_at)}</td>
        </tr>`).join('')}
      </tbody></table>`;
    }).catch((err) => toast(err.message, true));

    api(`${API}/audit-log?limit=40`).then((rows) => {
      const el = root.querySelector('[data-audit-log]');
      if (!el) return;
      if (!rows.length) { el.innerHTML = '<p class="empty-state">No activity recorded yet.</p>'; return; }
      el.innerHTML = `<div class="activity-feed">${rows.map((r) => activityRowHTML(r)).join('')}</div>`;
    }).catch((err) => toast(err.message, true));
  }

  // =========================================================================
  // INVOICES
  // =========================================================================
  function initInvoices() {
    const form = document.querySelector('[data-invoice-form]');
    if (!form) return;

    const el = {
      currencyTabs: document.querySelectorAll('[data-currency-tabs] .currency-tab'),
      bankArea: document.querySelector('[data-bank-account-area]'),
      recipientName: document.querySelector('#recipientName'),
      recipientAddress: document.querySelector('#recipientAddress'),
      recipientEmail: document.querySelector('#recipientEmail'),
      recipientPhone: document.querySelector('#recipientPhone'),
      amount: document.querySelector('#amount'),
      purposeNotes: document.querySelector('#purposeNotes'),
      polishBtn: document.querySelector('[data-polish-btn]'),
      aiBox: document.querySelector('[data-ai-box]'),
      officialPurpose: document.querySelector('#officialPurpose'),
      coverMessage: document.querySelector('#coverMessage'),
      saveDraftBtn: document.querySelector('[data-save-draft]'),
      generatePdfBtn: document.querySelector('[data-generate-pdf]'),
      previewStep: document.querySelector('[data-preview-step]'),
      previewFrame: document.querySelector('[data-preview-frame]'),
      sendStep: document.querySelector('[data-send-step]'),
      sendArea: document.querySelector('[data-send-area]'),
      sendEmailCheck: document.querySelector('#sendEmailCheck'),
      sendEmailAddr: document.querySelector('#sendEmailAddr'),
      sendWhatsappCheck: document.querySelector('#sendWhatsappCheck'),
      sendWhatsappNum: document.querySelector('#sendWhatsappNum'),
      downloadPdfBtn: document.querySelector('[data-download-pdf]'),
      sendNowBtn: document.querySelector('[data-send-now]'),
      statusPill: document.querySelector('[data-status-pill]'),
      formTitle: document.querySelector('[data-form-title]'),
      formSub: document.querySelector('[data-form-sub]'),
      historyEl: document.querySelector('[data-invoice-history]'),
      newInvoiceBtn: document.querySelector('[data-new-invoice]')
    };

    let currentCurrency = 'TZS';
    let currentInvoiceId = null;
    const accountsByCurrency = { TZS: [], USD: [] };
    const selectedAccountId = { TZS: null, USD: null };
    let bankEditingOpen = false;

    function money(n, currency) {
      const num = Number(n || 0).toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 });
      return `${currency} ${num}`;
    }

    function statusBadge(status) {
      if (status === 'paid') return '<span class="badge badge-success">Paid</span>';
      if (status === 'sent') return '<span class="badge badge-success">Sent</span>';
      return '<span class="badge badge-muted">Draft</span>';
    }

    // ---------------- Bank accounts ----------------
    function selectedAccount(currency) {
      const accs = accountsByCurrency[currency] || [];
      return accs.find((a) => a.id === selectedAccountId[currency]) || accs.find((a) => a.is_default) || accs[0] || null;
    }

    function renderBankArea(editing) {
      const currency = currentCurrency;
      const accs = accountsByCurrency[currency] || [];
      const acc = selectedAccount(currency);
      bankEditingOpen = !!editing;

      if (!editing) {
        if (!acc) {
          el.bankArea.innerHTML = `
            <div class="bank-account-empty">
              No ${currency} account saved yet — add one below and it'll be suggested automatically next time.
            </div>
            <button type="button" class="btn btn-ghost bank-account-edit-toggle" data-bank-edit-open>+ Add ${currency} account</button>
          `;
        } else {
          el.bankArea.innerHTML = `
            <div class="bank-account-box">
              <div class="grid2">
                <div><span class="k">Bank</span><span class="v">${escapeHtml(acc.bank_name)}</span></div>
                <div><span class="k">Account Name</span><span class="v">${escapeHtml(acc.account_name)}</span></div>
                <div><span class="k">Account Number</span><span class="v">${escapeHtml(acc.account_number)}</span></div>
                <div><span class="k">Currency</span><span class="v">${currency}</span></div>
              </div>
            </div>
            <div class="row-actions" style="margin-top:10px;">
              <button type="button" class="btn btn-ghost bank-account-edit-toggle" data-bank-edit-open>✎ Edit account</button>
              <button type="button" class="btn btn-ghost bank-account-edit-toggle" data-bank-add-new>+ Add another</button>
            </div>
          `;
        }
        el.bankArea.querySelector('[data-bank-edit-open]')?.addEventListener('click', () => renderBankArea('edit'));
        el.bankArea.querySelector('[data-bank-add-new]')?.addEventListener('click', () => renderBankArea('new'));
        return;
      }

      // editing === 'edit' (edit the currently selected account) or 'new' (blank form for a fresh account)
      const isNew = editing === 'new' || !acc;
      const prefill = isNew ? { bank_name: '', account_name: '', account_number: '' } : acc;
      const switcher = accs.length > 1 ? `
        <label for="bankAccountSwitch">Saved ${currency} accounts</label>
        <select id="bankAccountSwitch">
          ${accs.map((a) => `<option value="${a.id}" ${!isNew && a.id === (acc && acc.id) ? 'selected' : ''}>${escapeHtml(a.bank_name)} — ${escapeHtml(a.account_number)}${a.is_default ? ' (default)' : ''}</option>`).join('')}
        </select>
      ` : '';

      el.bankArea.innerHTML = `
        <div class="bank-account-box" style="border-style:dashed;">
          ${switcher}
          <div class="row2" style="margin-top:${switcher ? '12px' : '0'};">
            <div>
              <label for="bankNameInput">Bank Name</label>
              <input type="text" id="bankNameInput" value="${escapeHtml(prefill.bank_name)}" placeholder="e.g. Equity Bank">
            </div>
            <div>
              <label for="bankAccountNameInput">Account Name</label>
              <input type="text" id="bankAccountNameInput" value="${escapeHtml(prefill.account_name)}" placeholder="e.g. Yellow Stone Traders">
            </div>
          </div>
          <div style="margin-top:12px;">
            <label for="bankAccountNumberInput">Account Number</label>
            <input type="text" id="bankAccountNumberInput" value="${escapeHtml(prefill.account_number)}" placeholder="Account number">
          </div>
          <label style="display:flex; align-items:center; gap:8px; margin-top:12px; font-size:.82rem; font-weight:600; color:var(--navy);">
            <input type="checkbox" id="bankMakeDefault" style="width:16px;height:16px;" ${accs.length === 0 ? 'checked disabled' : ''}>
            Suggest this account by default for ${currency} invoices
          </label>
          <div class="row-actions" style="margin-top:14px;">
            <button type="button" class="btn btn-ghost" data-bank-cancel>Cancel</button>
            <button type="button" class="btn btn-primary" data-bank-save>💾 Save account</button>
          </div>
        </div>
      `;

      const switchEl = el.bankArea.querySelector('#bankAccountSwitch');
      if (switchEl) {
        switchEl.addEventListener('change', () => {
          const chosen = accs.find((a) => String(a.id) === switchEl.value);
          if (chosen) {
            el.bankArea.querySelector('#bankNameInput').value = chosen.bank_name;
            el.bankArea.querySelector('#bankAccountNameInput').value = chosen.account_name;
            el.bankArea.querySelector('#bankAccountNumberInput').value = chosen.account_number;
            selectedAccountId[currency] = chosen.id;
          }
        });
      }

      el.bankArea.querySelector('[data-bank-cancel]').addEventListener('click', () => renderBankArea(false));
      el.bankArea.querySelector('[data-bank-save]').addEventListener('click', async () => {
        const bankName = el.bankArea.querySelector('#bankNameInput').value.trim();
        const accountName = el.bankArea.querySelector('#bankAccountNameInput').value.trim();
        const accountNumber = el.bankArea.querySelector('#bankAccountNumberInput').value.trim();
        const makeDefault = el.bankArea.querySelector('#bankMakeDefault').checked;
        if (!bankName || !accountName || !accountNumber) {
          toast('Fill in bank name, account name and account number.', true);
          return;
        }
        try {
          let saved;
          const editingExistingId = !isNew && switchEl ? Number(switchEl.value) : (!isNew && acc ? acc.id : null);
          if (editingExistingId) {
            saved = await api(`${API}/bank-accounts/${editingExistingId}`, {
              method: 'PUT',
              body: JSON.stringify({ bankName, accountName, accountNumber, makeDefault })
            });
          } else {
            saved = await api(`${API}/bank-accounts`, {
              method: 'POST',
              body: JSON.stringify({ currency, bankName, accountName, accountNumber, makeDefault })
            });
          }
          toast('Bank account saved.');
          await loadBankAccounts();
          selectedAccountId[currency] = saved.id;
          renderBankArea(false);
        } catch (err) { toast(err.message, true); }
      });
    }

    async function loadBankAccounts() {
      try {
        const rows = await api(`${API}/bank-accounts`);
        accountsByCurrency.TZS = rows.filter((r) => r.currency === 'TZS');
        accountsByCurrency.USD = rows.filter((r) => r.currency === 'USD');
        ['TZS', 'USD'].forEach((c) => {
          const def = accountsByCurrency[c].find((a) => a.is_default) || accountsByCurrency[c][0];
          if (def && !selectedAccountId[c]) selectedAccountId[c] = def.id;
          const sub = document.querySelector(`[data-currency-sub="${c}"]`);
          if (sub) sub.textContent = def ? `${def.bank_name} · ${def.account_number}` : 'No account saved yet';
        });
        renderBankArea(bankEditingOpen ? (accountsByCurrency[currentCurrency].length ? 'edit' : 'new') : false);
      } catch (err) { toast(err.message, true); }
    }

    el.currencyTabs.forEach((tab) => {
      tab.addEventListener('click', () => {
        el.currencyTabs.forEach((t) => t.classList.toggle('active', t === tab));
        currentCurrency = tab.getAttribute('data-currency');
        renderBankArea(false);
      });
    });

    // ---------------- Save / Generate / Polish ----------------
    function gatherPayload() {
      return {
        currency: currentCurrency,
        bankAccountId: selectedAccountId[currentCurrency],
        recipientName: el.recipientName.value.trim(),
        recipientAddress: el.recipientAddress.value.trim(),
        recipientEmail: el.recipientEmail.value.trim(),
        recipientPhone: el.recipientPhone.value.trim(),
        amount: Number(el.amount.value || 0),
        purpose: el.purposeNotes.value.trim(),
        officialPurpose: el.officialPurpose.value.trim(),
        coverMessage: el.coverMessage.value.trim()
      };
    }

    function validateBasics(payload) {
      if (!payload.recipientName) { toast('Add a recipient name / company.', true); return false; }
      if (!payload.amount || payload.amount <= 0) { toast('Enter an amount greater than zero.', true); return false; }
      return true;
    }

    async function ensureInvoice(status) {
      const payload = gatherPayload();
      if (!validateBasics(payload)) return null;
      payload.status = status;
      try {
        let row;
        if (currentInvoiceId) {
          row = await api(`${API}/invoices/${currentInvoiceId}`, { method: 'PUT', body: JSON.stringify(payload) });
        } else {
          row = await api(`${API}/invoices`, { method: 'POST', body: JSON.stringify(payload) });
          currentInvoiceId = row.id;
        }
        applyInvoiceToUI(row);
        return row;
      } catch (err) { toast(err.message, true); return null; }
    }

    function applyInvoiceToUI(row) {
      el.statusPill.innerHTML = statusBadge(row.status);
      el.formTitle.textContent = `Invoice ${row.invoice_number}`;
      el.formSub.textContent = 'Editing an existing invoice — changes save when you generate the PDF again.';
    }

    el.saveDraftBtn.addEventListener('click', async () => {
      const row = await ensureInvoice('draft');
      if (row) { toast('Draft saved.'); loadHistory(); }
    });

    el.generatePdfBtn.addEventListener('click', async () => {
      const row = await ensureInvoice(currentInvoiceId ? undefined : 'draft');
      if (!row) return;
      el.previewStep.hidden = false;
      el.previewFrame.hidden = false;
      el.previewFrame.src = `${API}/invoices/${row.id}/pdf?t=${Date.now()}`;
      el.sendStep.hidden = false;
      el.sendArea.hidden = false;
      if (row.recipient_email) el.sendEmailAddr.value = row.recipient_email;
      if (row.recipient_phone) el.sendWhatsappNum.value = row.recipient_phone;
      toast('Invoice PDF generated below.');
      loadHistory();
    });

    el.polishBtn.addEventListener('click', async () => {
      if (!el.purposeNotes.value.trim()) { toast('Add a few rough notes first.', true); return; }
      const row = await ensureInvoice('draft');
      if (!row) return;
      el.polishBtn.disabled = true;
      el.polishBtn.textContent = 'Polishing…';
      try {
        const result = await api(`${API}/invoices/${row.id}/polish`, {
          method: 'POST',
          body: JSON.stringify({ notes: el.purposeNotes.value.trim() })
        });
        el.officialPurpose.value = result.officialPurpose || '';
        el.coverMessage.value = result.coverMessage || '';
        el.aiBox.hidden = false;
        toast('Purpose and cover message rewritten — review before sending.');
      } catch (err) { toast(err.message, true); }
      el.polishBtn.disabled = false;
      el.polishBtn.textContent = '✨ Polish purpose & message with AI';
    });

    // ---------------- Send ----------------
    el.downloadPdfBtn.addEventListener('click', (e) => {
      if (!currentInvoiceId) { e.preventDefault(); toast('Generate the PDF first.', true); return; }
      el.downloadPdfBtn.href = `${API}/invoices/${currentInvoiceId}/pdf`;
    });

    el.sendNowBtn.addEventListener('click', async () => {
      if (!currentInvoiceId) { toast('Generate the PDF first.', true); return; }
      const wantsEmail = el.sendEmailCheck.checked;
      const wantsWhatsapp = el.sendWhatsappCheck.checked;
      if (!wantsEmail && !wantsWhatsapp) { toast('Tick Email and/or WhatsApp first.', true); return; }

      el.sendNowBtn.disabled = true;
      el.sendNowBtn.textContent = 'Sending…';
      try {
        if (wantsEmail) {
          if (!el.sendEmailAddr.value.trim()) throw new Error("Add the recipient's email address.");
          const result = await api(`${API}/invoices/${currentInvoiceId}/send-email`, {
            method: 'POST', body: JSON.stringify({ to: el.sendEmailAddr.value.trim() })
          });
          if (result.sent) {
            toast('Emailed to ' + el.sendEmailAddr.value.trim() + '.');
          } else if (result.mailto) {
            window.open(result.mailto, '_blank');
            await api(`${API}/invoices/${currentInvoiceId}`, { method: 'PUT', body: JSON.stringify({ markEmailSent: true, status: 'sent' }) });
            toast('Opened in your email app — marked as sent.');
          }
        }
        if (wantsWhatsapp) {
          if (!el.sendWhatsappNum.value.trim()) throw new Error("Add the recipient's WhatsApp number.");
          const result = await api(`${API}/invoices/${currentInvoiceId}/send-whatsapp`, {
            method: 'POST', body: JSON.stringify({ phone: el.sendWhatsappNum.value.trim() })
          });
          if (result.sent) {
            toast('Sent via WhatsApp.');
          } else if (result.waLink) {
            window.open(result.waLink, '_blank');
            await api(`${API}/invoices/${currentInvoiceId}`, { method: 'PUT', body: JSON.stringify({ markWhatsappSent: true, status: 'sent' }) });
            toast('Opened WhatsApp with the message ready — marked as sent.');
          }
        }
        const fresh = await api(`${API}/invoices/${currentInvoiceId}`);
        applyInvoiceToUI(fresh);
        loadHistory();
      } catch (err) { toast(err.message, true); }
      el.sendNowBtn.disabled = false;
      el.sendNowBtn.textContent = 'Send Now';
    });

    // ---------------- New invoice / reset ----------------
    function resetForm() {
      currentInvoiceId = null;
      form.reset();
      el.aiBox.hidden = true;
      el.previewStep.hidden = true;
      el.previewFrame.hidden = true;
      el.previewFrame.src = '';
      el.sendStep.hidden = true;
      el.sendArea.hidden = true;
      el.sendEmailCheck.checked = false;
      el.sendWhatsappCheck.checked = false;
      el.statusPill.innerHTML = statusBadge('draft');
      el.formTitle.textContent = 'New Invoice';
      el.formSub.textContent = 'Fill in the details below, then generate and send the PDF.';
      renderBankArea(false);
    }
    el.newInvoiceBtn.addEventListener('click', resetForm);

    // ---------------- History ----------------
    async function loadHistory() {
      try {
        const rows = await api(`${API}/invoices`);
        if (!rows.length) {
          el.historyEl.innerHTML = '<p class="empty-state">No invoices yet — generate your first one on the left.</p>';
          return;
        }
        el.historyEl.innerHTML = rows.map((r) => `
          <div class="invoice-history-item" data-id="${r.id}">
            <div class="row-top">
              <span class="inv-no">${escapeHtml(r.invoice_number)}</span>
              <span class="inv-amount">${money(r.amount, r.currency)}</span>
            </div>
            <div class="row-bottom">
              <span class="inv-recipient">${escapeHtml(r.recipient_name)}</span>
              <span class="inv-meta">${statusBadge(r.status)} · ${timeAgo(r.updated_at)}</span>
            </div>
            <div class="inv-actions">
              <button type="button" data-load-invoice>Open</button>
              <a href="${API}/invoices/${r.id}/pdf" target="_blank" rel="noopener">View PDF</a>
              <button type="button" data-delete-invoice>Delete</button>
            </div>
          </div>
        `).join('');
      } catch (err) { toast(err.message, true); }
    }

    el.historyEl.addEventListener('click', async (e) => {
      const card = e.target.closest('[data-id]');
      if (!card) return;
      const id = Number(card.getAttribute('data-id'));

      if (e.target.matches('[data-delete-invoice]')) {
        if (!confirm('Delete this invoice? This cannot be undone.')) return;
        try {
          await api(`${API}/invoices/${id}`, { method: 'DELETE' });
          toast('Invoice deleted.');
          if (currentInvoiceId === id) resetForm();
          loadHistory();
        } catch (err) { toast(err.message, true); }
        return;
      }

      if (e.target.matches('[data-load-invoice]')) {
        try {
          const row = await api(`${API}/invoices/${id}`);
          currentInvoiceId = row.id;
          currentCurrency = row.currency;
          el.currencyTabs.forEach((t) => t.classList.toggle('active', t.getAttribute('data-currency') === row.currency));
          if (row.bank_account_id) selectedAccountId[row.currency] = row.bank_account_id;
          renderBankArea(false);
          el.recipientName.value = row.recipient_name || '';
          el.recipientAddress.value = row.recipient_address || '';
          el.recipientEmail.value = row.recipient_email || '';
          el.recipientPhone.value = row.recipient_phone || '';
          el.amount.value = row.amount || '';
          el.purposeNotes.value = row.purpose || '';
          if (row.official_purpose || row.cover_message) {
            el.officialPurpose.value = row.official_purpose || '';
            el.coverMessage.value = row.cover_message || '';
            el.aiBox.hidden = false;
          } else {
            el.aiBox.hidden = true;
          }
          applyInvoiceToUI(row);
          el.previewStep.hidden = false;
          el.previewFrame.hidden = false;
          el.previewFrame.src = `${API}/invoices/${row.id}/pdf?t=${Date.now()}`;
          el.sendStep.hidden = false;
          el.sendArea.hidden = false;
          el.sendEmailAddr.value = row.recipient_email || '';
          el.sendWhatsappNum.value = row.recipient_phone || '';
          window.scrollTo({ top: 0, behavior: 'smooth' });
        } catch (err) { toast(err.message, true); }
      }
    });

    loadBankAccounts();
    loadHistory();
  }
})();
