---
import Base from '../../layouts/Base.astro';
import IdeasPage from '../../components/IdeasPage.svelte';
const baseUrl = import.meta.env.BASE_URL ?? '/';
---
<Base title="Support — BincioActivity">
  <div class="max-w-2xl mx-auto">
    <h1 class="text-2xl font-bold text-white mb-1">Support</h1>
    <p class="text-sm text-zinc-500 mb-5">Open-source, self-hosted activity tracking</p>

    <!-- Tab bar -->
    <div class="flex gap-1 border-b border-zinc-800 mb-6 overflow-x-auto">
      <button data-tab="donate"    class="tab-btn px-4 py-2 text-sm font-medium rounded-t transition-colors shrink-0">Donate</button>
      <button data-tab="ideas"     class="tab-btn px-4 py-2 text-sm font-medium rounded-t transition-colors shrink-0">Ideas</button>
      <button data-tab="feedback"  class="tab-btn px-4 py-2 text-sm font-medium rounded-t transition-colors shrink-0">Feedback</button>
      <button data-tab="community" class="tab-btn px-4 py-2 text-sm font-medium rounded-t transition-colors shrink-0">Community</button>
      <button data-tab="about"     class="tab-btn px-4 py-2 text-sm font-medium rounded-t transition-colors shrink-0">About</button>
    </div>

    <!-- ── DONATE ─────────────────────────────────────────────────────── -->
    <div id="tab-donate" class="tab-panel">
      <div class="flex flex-wrap gap-2 mb-4">
        <a href="https://ko-fi.com/brutsalvadi" target="_blank" rel="noopener noreferrer"
           class="inline-flex items-center gap-2 px-4 py-2 rounded-lg text-sm font-medium hover:opacity-90 transition-opacity"
           style="background:#FF5E5B; color:#fff;">
          ☕ Support on Ko-fi
        </a>
        <div class="relative">
          <a href="https://web.satispay.com/download/qrcode/S6Y-CON--BE9BD345-4499-4C1D-9AC3-D62FC5FF0AD4"
             target="_blank" rel="noopener noreferrer"
             class="inline-flex items-center gap-2 px-4 py-2 rounded-lg text-sm font-medium hover:opacity-90 transition-opacity"
             style="background:#E3162C; color:#fff;">
            Satispay @brutsalvadi
          </a>
          <span class="absolute -top-2 -right-2 px-1.5 py-0.5 rounded-full text-[10px] font-semibold bg-green-500 text-white leading-none">
            preferred
          </span>
        </div>
      </div>
      <div class="mb-8">
        <img src="/satispay-qr.jpg" alt="Satispay QR code — @brutsalvadi" class="w-36 h-36 rounded-xl" />
      </div>

      <!-- Budget section -->
      <div class="border-t border-zinc-800 pt-6">
        <div class="flex items-center justify-between mb-4">
          <div>
            <h2 class="text-base font-semibold text-white">Running costs</h2>
            <p class="text-xs text-zinc-500 mt-0.5">Transparent breakdown of donations and expenses</p>
          </div>
          <div id="budget-admin-controls" class="hidden flex gap-2">
            <button id="budget-set-goal-btn"
              class="px-3 py-1.5 rounded-lg bg-zinc-800 hover:bg-zinc-700 text-zinc-300 hover:text-white text-xs font-medium transition-colors border border-zinc-700">
              Set monthly goal
            </button>
            <button id="budget-add-btn"
              class="px-3 py-1.5 rounded-lg bg-[--accent] hover:opacity-90 text-white text-xs font-medium transition-opacity">
              + Add entry
            </button>
          </div>
        </div>

        <!-- Set-goal form (admin, hidden by default) -->
        <form id="budget-goal-form" class="hidden mb-4 p-3 rounded-xl bg-zinc-900 border border-zinc-800 text-sm">
          <label class="block text-zinc-400 mb-1 text-xs">Monthly goal (€)</label>
          <div class="flex gap-2">
            <input id="budget-goal-input" type="number" min="0" step="0.01" placeholder="e.g. 20"
              class="flex-1 px-3 py-1.5 rounded-lg bg-zinc-800 border border-zinc-700 text-white text-sm focus:outline-none focus:border-[--accent]" />
            <button type="submit" class="px-4 py-1.5 rounded-lg bg-[--accent] hover:opacity-90 text-white text-sm font-medium transition-opacity">Save</button>
            <button type="button" id="budget-goal-cancel" class="px-3 py-1.5 rounded-lg bg-zinc-800 hover:bg-zinc-700 text-zinc-400 text-sm transition-colors">Cancel</button>
          </div>
          <p id="budget-goal-err" class="text-red-400 text-xs mt-1 hidden"></p>
        </form>

        <!-- Add-entry form (admin, hidden by default) -->
        <form id="budget-add-form" class="hidden mb-4 p-3 rounded-xl bg-zinc-900 border border-zinc-800 text-sm space-y-2">
          <div class="flex gap-2">
            <button type="button" data-type="donation" class="entry-type-btn flex-1 py-1.5 rounded-lg border text-xs font-medium transition-colors">💚 Donation</button>
            <button type="button" data-type="expense"  class="entry-type-btn flex-1 py-1.5 rounded-lg border text-xs font-medium transition-colors">🔴 Expense</button>
          </div>
          <input id="entry-label" type="text" placeholder="Label (e.g. VPS hosting)"
            class="w-full px-3 py-1.5 rounded-lg bg-zinc-800 border border-zinc-700 text-white text-sm focus:outline-none focus:border-[--accent]" />
          <div class="flex gap-2">
            <div class="flex-1 relative">
              <span class="absolute left-3 top-1/2 -translate-y-1/2 text-zinc-500 text-sm">€</span>
              <input id="entry-amount" type="number" min="0" step="0.01" placeholder="0.00"
                class="w-full pl-7 pr-3 py-1.5 rounded-lg bg-zinc-800 border border-zinc-700 text-white text-sm focus:outline-none focus:border-[--accent]" />
            </div>
            <input id="entry-month" type="month"
              class="flex-1 px-3 py-1.5 rounded-lg bg-zinc-800 border border-zinc-700 text-white text-sm focus:outline-none focus:border-[--accent] [color-scheme:dark]" />
          </div>
          <input id="entry-note" type="text" placeholder="Note (optional)"
            class="w-full px-3 py-1.5 rounded-lg bg-zinc-800 border border-zinc-700 text-white text-sm focus:outline-none focus:border-[--accent]" />
          <label class="flex items-center gap-2 text-xs text-zinc-400 cursor-pointer select-none">
            <input id="entry-recurring" type="checkbox" class="accent-[--accent] w-3.5 h-3.5" />
            Repeat every month
          </label>
          <p id="budget-add-err" class="text-red-400 text-xs hidden"></p>
          <div class="flex gap-2 pt-1">
            <button type="submit" class="px-4 py-1.5 rounded-lg bg-[--accent] hover:opacity-90 text-white text-sm font-medium transition-opacity">Add</button>
            <button type="button" id="budget-add-cancel" class="px-3 py-1.5 rounded-lg bg-zinc-800 hover:bg-zinc-700 text-zinc-400 text-sm transition-colors">Cancel</button>
          </div>
        </form>

        <div id="budget-list">
          <p class="text-zinc-500 text-sm text-center py-6">Loading…</p>
        </div>
      </div>
    </div>

    <!-- ── IDEAS ────────────────────────────────────────────────────────── -->
    <div id="tab-ideas" class="tab-panel" style="display:none">
      <IdeasPage client:only="svelte" />
    </div>

    <!-- ── ABOUT ──────────────────────────────────────────────────────── -->
    <div id="tab-about" class="tab-panel" style="display:none">
      <div class="flex justify-end gap-3 text-xs text-zinc-500 mb-5">
        <span class="text-zinc-300 font-medium">EN</span>
        <a href={`${baseUrl}about/it/`} class="hover:text-white transition-colors">IT</a>
        <a href={`${baseUrl}about/es/`} class="hover:text-white transition-colors">ES</a>
        <a href={`${baseUrl}about/ca/`} class="hover:text-white transition-colors">CA</a>
      </div>
      <div class="space-y-8 text-sm text-zinc-400 leading-relaxed">
        <section>
          <h2 class="text-base font-semibold text-white mb-2">What is this?</h2>
          <p>
            BincioActivity is a free, open-source platform for tracking your outdoor activities —
            cycling, running, hiking, and more. It is designed to be self-hosted: you (or someone
            you trust) run the server, and your data stays under your control.
          </p>
          <p class="mt-2">
            Activities are stored in an open JSON format called BAS (BincioActivity Schema),
            which is designed to be readable and portable. The platform has no hidden analytics,
            no advertising, and no third-party data sharing.
          </p>
        </section>

        <section>
          <h2 class="text-base font-semibold text-white mb-2">Joining &amp; invitations</h2>
          <p>
            This instance is invite-only. To join, you need an invite link from an existing
            member — each link is single-use and tied to a unique code.
          </p>
          <p class="mt-2">
            Once you have an account, you can generate up to <strong class="text-zinc-300">3 invite links</strong> to
            share with people you trust. You can manage your invites from the
            <a href="/invites/" class="text-blue-400 hover:text-blue-300 transition-colors">invites page</a>.
          </p>
        </section>

        <section>
          <h2 class="text-base font-semibold text-white mb-2">Your data on this server</h2>
          <p>
            When you upload a FIT, GPX, or TCX file, the server converts it to BAS format.
            By default the original source file is also kept in your account's
            <code class="text-zinc-300 bg-zinc-800 px-1 rounded">originals/</code> folder.
            You can opt out of this at upload time by unchecking <em>"Keep original file on server"</em>.
          </p>
          <p class="mt-2">
            Keeping originals is recommended during these early stages of the project: if the
            processing pipeline improves (better elevation smoothing, speed calculation, lap
            detection, etc.) you can re-import your files to take advantage of the changes.
          </p>
        </section>

        <section>
          <h2 class="text-base font-semibold text-white mb-2">Early-stage software</h2>
          <p>
            BincioActivity is under active development. The data format, processing pipeline,
            and server API may change between versions. Breaking changes are possible, especially
            at this stage. When they occur, re-importing your original files is the safest way
            to bring your data up to date.
          </p>
          <p class="mt-2">
            There is no guarantee of uptime, data integrity, or forward compatibility for
            any particular version. Use this software at your own risk, and keep your own
            backups of important data.
          </p>
        </section>

        <section class="border border-zinc-800 rounded-xl p-4 bg-zinc-900/50">
          <h2 class="text-base font-semibold text-white mb-2">Disclaimer</h2>
          <p>
            BincioActivity is provided <strong class="text-zinc-300">"as is"</strong>, without
            warranty of any kind. The authors and server operators accept no responsibility for:
          </p>
          <ul class="list-disc list-inside mt-2 space-y-1">
            <li>Loss, corruption, or unauthorised access to your activity data</li>
            <li>Data exposed through misconfiguration of the server or infrastructure</li>
            <li>Inaccuracies in computed statistics (distance, elevation, heart rate, etc.)</li>
            <li>Any consequences of acting on information displayed by this application</li>
          </ul>
          <p class="mt-3">
            You are responsible for securing your account with a strong password, reviewing
            what data you share, and making your own backups. GPS and health data can be
            sensitive — think carefully about what you upload and who can see it.
          </p>
        </section>

        <section>
          <h2 class="text-base font-semibold text-white mb-2">Open source</h2>
          <p>
            BincioActivity is open-source software. You are free to inspect the code,
            self-host your own instance, and contribute improvements.
          </p>
        </section>
      </div>
    </div>

    <!-- ── COMMUNITY ──────────────────────────────────────────────────── -->
    <div id="tab-community" class="tab-panel" style="display:none">
      <p id="community-summary" class="text-zinc-500 text-xs mb-4"></p>
      <div id="community-tree" class="text-sm"></div>
      <p id="community-empty" class="text-zinc-500 text-sm text-center py-6" style="display:none">Community stats not available.</p>
    </div>

    <!-- ── FEEDBACK ───────────────────────────────────────────────────── -->
    <div id="tab-feedback" class="tab-panel" style="display:none">
      <h2 class="text-base font-semibold text-white mb-1">Send feedback</h2>
      <p class="text-sm text-zinc-500 mb-5">Report a bug, suggest a feature, or share anything useful.</p>

      <form id="feedback-form" class="space-y-4">
        <textarea id="fb-text" rows="6" placeholder="What's on your mind?"
          class="w-full px-3 py-2 rounded-lg bg-zinc-900 border border-zinc-700 text-white placeholder-zinc-500 text-sm focus:outline-none focus:border-[--accent] resize-none"></textarea>

        <div>
          <p class="text-xs text-zinc-500 mb-2">Attach up to 3 screenshots (max 2 MB each)</p>
          <div id="fb-drop"
            class="border-2 border-dashed border-zinc-700 rounded-lg p-5 text-center text-zinc-500 text-sm cursor-pointer hover:border-zinc-500 hover:text-zinc-300 transition-colors">
            <span>Drop images or click to browse</span>
            <input id="fb-input" type="file" accept="image/*" multiple class="hidden" />
          </div>
          <div id="fb-previews" class="flex gap-2 flex-wrap mt-2"></div>
        </div>

        <p id="fb-error" class="text-red-400 text-sm hidden"></p>
        <button type="submit"
          class="w-full py-2 rounded-lg bg-[--accent] hover:opacity-90 text-white font-medium text-sm transition-opacity">
          Send feedback
        </button>
      </form>

      <div id="fb-success" class="hidden text-center mt-12">
        <p class="text-2xl mb-2">Thanks!</p>
        <p class="text-zinc-400 text-sm">Your feedback has been received.</p>
      </div>
    </div>

  </div>
</Base>

<script>
// ── Tabs ──────────────────────────────────────────────────────────────────────
const TABS = ['donate', 'ideas', 'feedback', 'community', 'about'] as const;
type TabName = typeof TABS[number];

function showTab(name: TabName) {
  TABS.forEach(t => {
    const panel = document.getElementById(`tab-${t}`)!;
    const btn   = document.querySelector<HTMLElement>(`.tab-btn[data-tab="${t}"]`)!;
    const active = t === name;
    panel.style.display = active ? '' : 'none';
    btn.classList.toggle('text-white',        active);
    btn.classList.toggle('border-b-2',        active);
    btn.classList.toggle('border-[--accent]', active);
    btn.classList.toggle('-mb-px',            active);
    btn.classList.toggle('text-zinc-400',    !active);
  });
  if (history.replaceState) history.replaceState(null, '', '#' + name);
}

const hash = location.hash.slice(1) as TabName;
showTab(TABS.includes(hash) ? hash : 'donate');
document.querySelectorAll<HTMLElement>('.tab-btn').forEach(btn => {
  btn.addEventListener('click', () => showTab(btn.dataset.tab as TabName));
});

// ── Community tree ────────────────────────────────────────────────────────────
let communityLoaded = false;

async function loadCommunity() {
  const summaryEl = document.getElementById('community-summary')!;
  const treeEl    = document.getElementById('community-tree')!;
  const emptyEl   = document.getElementById('community-empty')!;
  let data: any;
  try {
    const r = await fetch('/api/stats');
    if (!r.ok) { emptyEl.style.display = ''; return; }
    data = await r.json();
  } catch { emptyEl.style.display = ''; return; }
  if (!data.user_count) { emptyEl.style.display = ''; return; }

  const n = data.user_count;
  summaryEl.textContent = `${n} ${n === 1 ? 'member' : 'members'}`;

  const byHandle: Record<string, any> = {};
  for (const m of data.members) byHandle[m.handle] = m;
  const children: Record<string, string[]> = {};
  const roots: string[] = [];
  for (const m of data.members) {
    if (m.invited_by && byHandle[m.invited_by]) {
      (children[m.invited_by] ??= []).push(m.handle);
    } else {
      roots.push(m.handle);
    }
  }

  function formatDays(days: number) {
    if (days < 1) return '< 1 day';
    if (days < 30) return `${days} ${days === 1 ? 'day' : 'days'}`;
    const mo = Math.floor(days / 30);
    return mo === 1 ? '1 mo' : `${mo} mo`;
  }

  function renderNode(handle: string, depth: number) {
    const m = byHandle[handle];
    const row = document.createElement('div');
    row.className = 'flex items-baseline gap-2 py-1.5 border-b border-zinc-800/50';
    row.style.paddingLeft = `${depth * 20}px`;
    if (depth > 0) {
      const c = document.createElement('span');
      c.className = 'text-zinc-700 shrink-0';
      c.textContent = '└';
      row.appendChild(c);
    }
    const name = document.createElement('span');
    name.className = 'text-white font-medium';
    name.textContent = m.display_name || `@${handle}`;
    row.appendChild(name);
    const hEl = document.createElement('span');
    hEl.className = 'text-zinc-600 text-xs';
    hEl.textContent = `@${handle}`;
    row.appendChild(hEl);
    const spacer = document.createElement('span');
    spacer.className = 'flex-1';
    row.appendChild(spacer);
    const meta = document.createElement('span');
    meta.className = 'text-zinc-600 text-xs text-right shrink-0';
    const sub = m.invited_by ? `invited by @${m.invited_by}` : 'founder';
    meta.innerHTML = `${formatDays(m.member_for_days)}<br><span class="text-zinc-700">${sub}</span>`;
    row.appendChild(meta);
    treeEl.appendChild(row);
    for (const child of (children[handle] ?? [])) renderNode(child, depth + 1);
  }
  for (const root of roots) renderNode(root, 0);
}

document.querySelector<HTMLElement>('.tab-btn[data-tab="community"]')!.addEventListener('click', () => {
  if (!communityLoaded) { communityLoaded = true; loadCommunity(); }
});
if (location.hash.slice(1) === 'community') { communityLoaded = true; loadCommunity(); }

// ── Budget ────────────────────────────────────────────────────────────────────
let budgetData: { monthly_target_eur: number | null; entries: any[] } | null = null;
let isAdmin = false;
let addEntryType: 'donation' | 'expense' = 'donation';
const expandedMonths = new Set<string>();
let budgetFirstRender = true;

async function loadBudget() {
  try {
    const [br, mr] = await Promise.all([
      fetch('/api/budget'),
      fetch('/api/me', { credentials: 'include' }),
    ]);
    budgetData = br.ok ? await br.json() : { monthly_target_eur: null, entries: [] };
    if (mr.ok) { const me = await mr.json(); isAdmin = !!me.is_admin; }
  } catch {
    budgetData = { monthly_target_eur: null, entries: [] };
  }
  renderBudget();
}

function fmtEur(n: number) {
  return `€${n.toFixed(2).replace('.00', '')}`;
}

function monthLabel(ym: string) {
  const [y, m] = ym.split('-');
  return new Date(parseInt(y), parseInt(m) - 1, 1)
    .toLocaleString('default', { month: 'long', year: 'numeric' });
}

function renderBudget() {
  const listEl     = document.getElementById('budget-list')!;
  const adminCtrls = document.getElementById('budget-admin-controls')!;
  const goalInput  = document.getElementById('budget-goal-input') as HTMLInputElement;

  if (isAdmin) {
    adminCtrls.classList.remove('hidden');
    if (budgetData?.monthly_target_eur != null) goalInput.value = String(budgetData.monthly_target_eur);
  }

  const entries = budgetData?.entries ?? [];
  if (entries.length === 0) {
    listEl.innerHTML = `<p class="text-zinc-500 text-sm text-center py-6">${isAdmin ? 'No entries yet. Add the first one above.' : 'No budget entries yet.'}</p>`;
    return;
  }

  const byMonth: Record<string, any[]> = {};
  for (const e of entries) (byMonth[e.month] ??= []).push(e);
  const months = Object.keys(byMonth).sort().reverse();

  if (budgetFirstRender) { expandedMonths.add(months[0]); budgetFirstRender = false; }

  listEl.innerHTML = '';

  for (const ym of months) {
    const items   = byMonth[ym];
    const donated = items.filter(e => e.type === 'donation').reduce((s, e) => s + e.amount_eur, 0);
    const spent   = items.filter(e => e.type === 'expense' ).reduce((s, e) => s + e.amount_eur, 0);
    const balance = donated - spent;
    const target  = budgetData?.monthly_target_eur;
    const pct     = target ? Math.min(100, Math.round(donated / target * 100)) : null;
    const open    = expandedMonths.has(ym);

    const wrapper = document.createElement('div');
    wrapper.className = 'mb-2 rounded-xl border border-zinc-800 overflow-hidden';

    // ── Month header row ──────────────────────────────────────────────────
    const header = document.createElement('button');
    header.className = 'w-full flex items-center gap-2 px-3 py-2.5 bg-zinc-900 hover:bg-zinc-800/60 transition-colors text-sm';
    header.innerHTML = `
      <span class="font-medium text-white shrink-0">${monthLabel(ym)}</span>
      ${pct !== null
        ? `<div class="flex-1 mx-2 flex items-center gap-1.5"><div class="flex-1 h-1.5 rounded-full bg-zinc-800 overflow-hidden"><div class="h-full rounded-full" style="width:${pct}%;background:var(--accent)"></div></div><span class="text-xs text-zinc-500 shrink-0">${pct}%</span></div>`
        : `<span class="flex-1"></span>`}
      <span class="text-xs text-green-400"><span class="hidden sm:inline text-zinc-600 mr-0.5">donated</span>+${fmtEur(donated)}</span>
      <span class="text-xs text-zinc-700">/</span>
      <span class="text-xs text-red-400"><span class="hidden sm:inline text-zinc-600 mr-0.5">spent</span>−${fmtEur(spent)}</span>
      <span class="text-xs text-zinc-700">/</span>
      <span class="text-xs ${balance >= 0 ? 'text-zinc-300' : 'text-red-400'}"><span class="hidden sm:inline text-zinc-600 mr-0.5">balance</span>${balance >= 0 ? '+' : '−'}${fmtEur(Math.abs(balance))}</span>
      <span class="text-zinc-600 text-xs ml-1">${open ? '▲' : '▼'}</span>`;
    header.addEventListener('click', () => {
      if (expandedMonths.has(ym)) expandedMonths.delete(ym); else expandedMonths.add(ym);
      renderBudget();
    });
    wrapper.appendChild(header);

    // ── Expanded detail ───────────────────────────────────────────────────
    if (open) {
      const detail = document.createElement('div');
      detail.className = 'border-t border-zinc-800';

      const entriesDiv = document.createElement('div');
      entriesDiv.className = 'divide-y divide-zinc-800/60';
      let entriesHtml = '';
      for (const e of items) {
        const icon  = e.type === 'donation' ? '💚' : '🔴';
        const sign  = e.type === 'donation' ? '+' : '−';
        const color = e.type === 'donation' ? 'text-green-400' : 'text-red-400';
        const recurBadge = e.recurring
          ? `<span class="text-zinc-500 text-xs shrink-0" title="Repeats every month">↻</span>`
          : e.recurring_from
            ? `<span class="text-zinc-600 text-xs shrink-0" title="Auto-added (recurring)">↻</span>`
            : '';
        const adminBtns = isAdmin
          ? `<button data-edit="${e.id}" class="edit-btn text-zinc-600 hover:text-zinc-300 text-xs px-1 transition-colors">✎</button>
             <button data-del="${e.id}"  class="del-btn  text-zinc-700 hover:text-red-400  text-xs px-1 transition-colors">✕</button>`
          : '';
        entriesHtml += `<div class="flex items-center gap-3 px-3 py-2 bg-zinc-900 text-sm" data-entry-id="${e.id}">
          <span class="text-base shrink-0">${icon}</span>
          <span class="flex-1 text-zinc-300 min-w-0">${e.label}${e.note ? `<span class="text-zinc-600 text-xs ml-2">${e.note}</span>` : ''}</span>
          ${recurBadge}
          <span class="font-medium shrink-0 ${color}">${sign}${fmtEur(e.amount_eur)}</span>
          ${adminBtns}
        </div>`;
      }
      entriesDiv.innerHTML = entriesHtml;
      detail.appendChild(entriesDiv);
      wrapper.appendChild(detail);

      entriesDiv.querySelectorAll<HTMLElement>('.del-btn').forEach(btn => {
        btn.addEventListener('click', async () => {
          if (!confirm('Delete this entry?')) return;
          const r = await fetch(`/api/budget/entries/${btn.dataset.del}`, { method: 'DELETE', credentials: 'include' });
          if (r.ok) { budgetData!.entries = budgetData!.entries.filter(e => e.id !== btn.dataset.del); renderBudget(); }
        });
      });

      entriesDiv.querySelectorAll<HTMLElement>('.edit-btn').forEach(btn => {
        btn.addEventListener('click', () => {
          const id    = btn.dataset.edit!;
          const entry = budgetData!.entries.find(e => e.id === id);
          if (!entry) return;
          const row = entriesDiv.querySelector<HTMLElement>(`[data-entry-id="${id}"]`)!;
          row.innerHTML = `
            <form class="inline-edit-form flex flex-wrap gap-2 items-center w-full py-1" data-id="${id}">
              <select name="type" class="px-2 py-1 rounded bg-zinc-800 border border-zinc-700 text-white text-xs focus:outline-none">
                <option value="donation" ${entry.type==='donation'?'selected':''}>💚 Donation</option>
                <option value="expense"  ${entry.type==='expense' ?'selected':''}>🔴 Expense</option>
              </select>
              <input name="label"      value="${entry.label}"      placeholder="Label"
                class="flex-1 min-w-24 px-2 py-1 rounded bg-zinc-800 border border-zinc-700 text-white text-xs focus:outline-none" />
              <input name="amount_eur" type="number" value="${entry.amount_eur}" step="0.01" min="0"
                class="w-20 px-2 py-1 rounded bg-zinc-800 border border-zinc-700 text-white text-xs focus:outline-none" />
              <input name="month"      type="month"  value="${entry.month}"
                class="px-2 py-1 rounded bg-zinc-800 border border-zinc-700 text-white text-xs focus:outline-none [color-scheme:dark]" />
              <input name="note"       value="${entry.note ?? ''}"  placeholder="Note"
                class="flex-1 min-w-24 px-2 py-1 rounded bg-zinc-800 border border-zinc-700 text-white text-xs focus:outline-none" />
              <label class="flex items-center gap-1 text-xs text-zinc-400 cursor-pointer">
                <input type="checkbox" name="recurring" ${entry.recurring?'checked':''} class="accent-[--accent] w-3 h-3" />↻
              </label>
              <button type="submit" class="px-3 py-1 rounded bg-[--accent] text-white text-xs hover:opacity-90">Save</button>
              <button type="button" class="cancel-edit px-2 py-1 rounded bg-zinc-800 text-zinc-400 text-xs hover:bg-zinc-700">✕</button>
            </form>`;
          row.querySelector<HTMLFormElement>('.inline-edit-form')!.addEventListener('submit', async ev => {
            ev.preventDefault();
            const fd   = new FormData(ev.target as HTMLFormElement);
            const body = { type: fd.get('type'), label: fd.get('label'), amount_eur: parseFloat(fd.get('amount_eur') as string), month: fd.get('month'), note: fd.get('note'), recurring: fd.get('recurring') === 'on' };
            const r    = await fetch(`/api/budget/entries/${id}`, { method: 'PATCH', credentials: 'include', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body) });
            if (r.ok) { const updated = await r.json(); const idx = budgetData!.entries.findIndex(e => e.id === id); if (idx !== -1) budgetData!.entries[idx] = updated; renderBudget(); }
          });
          row.querySelector('.cancel-edit')!.addEventListener('click', renderBudget);
        });
      });
    }

    listEl.appendChild(wrapper);
  }
}

// Load budget on page load (donate is the default tab)
loadBudget();

// Goal form
const goalForm   = document.getElementById('budget-goal-form') as HTMLFormElement;
const goalBtn    = document.getElementById('budget-set-goal-btn')!;
const goalCancel = document.getElementById('budget-goal-cancel')!;
const goalErr    = document.getElementById('budget-goal-err')!;
goalBtn.addEventListener('click', () => goalForm.classList.toggle('hidden'));
goalCancel.addEventListener('click', () => goalForm.classList.add('hidden'));
goalForm.addEventListener('submit', async e => {
  e.preventDefault();
  goalErr.classList.add('hidden');
  const v = parseFloat((document.getElementById('budget-goal-input') as HTMLInputElement).value);
  const r = await fetch('/api/budget/settings', { method: 'POST', credentials: 'include', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ monthly_target_eur: isNaN(v) ? null : v }) });
  if (r.ok) { if (budgetData) budgetData.monthly_target_eur = isNaN(v) ? null : v; goalForm.classList.add('hidden'); renderBudget(); }
  else { goalErr.textContent = 'Failed to save goal.'; goalErr.classList.remove('hidden'); }
});

// Add-entry form
const addForm   = document.getElementById('budget-add-form') as HTMLFormElement;
const addBtn    = document.getElementById('budget-add-btn')!;
const addCancel = document.getElementById('budget-add-cancel')!;
const addErr    = document.getElementById('budget-add-err')!;

const now = new Date();
const defaultMonth = `${now.getFullYear()}-${String(now.getMonth()+1).padStart(2,'0')}`;
(document.getElementById('entry-month') as HTMLInputElement).value = defaultMonth;

addForm.querySelectorAll<HTMLButtonElement>('.entry-type-btn').forEach(btn => {
  btn.addEventListener('click', () => {
    addEntryType = btn.dataset.type as 'donation' | 'expense';
    addForm.querySelectorAll<HTMLButtonElement>('.entry-type-btn').forEach(b => {
      const a = b.dataset.type === addEntryType;
      b.classList.toggle('border-[--accent]', a); b.classList.toggle('text-white',      a); b.classList.toggle('bg-zinc-800',    a);
      b.classList.toggle('border-zinc-700', !a);  b.classList.toggle('text-zinc-400', !a);
    });
  });
});
(addForm.querySelector('.entry-type-btn[data-type="donation"]') as HTMLButtonElement).click();

addBtn.addEventListener('click',    () => { addForm.classList.toggle('hidden'); addErr.classList.add('hidden'); });
addCancel.addEventListener('click', () => addForm.classList.add('hidden'));

addForm.addEventListener('submit', async e => {
  e.preventDefault();
  addErr.classList.add('hidden');
  const label  = (document.getElementById('entry-label')  as HTMLInputElement).value.trim();
  const amount = parseFloat((document.getElementById('entry-amount') as HTMLInputElement).value);
  const month  = (document.getElementById('entry-month')  as HTMLInputElement).value;
  const note   = (document.getElementById('entry-note')   as HTMLInputElement).value.trim();
  if (!label)      { addErr.textContent = 'Label is required.';  addErr.classList.remove('hidden'); return; }
  if (isNaN(amount)) { addErr.textContent = 'Amount is required.'; addErr.classList.remove('hidden'); return; }
  const recurring = (document.getElementById('entry-recurring') as HTMLInputElement).checked;
  const r = await fetch('/api/budget/entries', { method: 'POST', credentials: 'include', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ type: addEntryType, label, amount_eur: amount, month, note, recurring: recurring || undefined }) });
  if (r.ok) {
    budgetData!.entries.push(await r.json());
    addForm.classList.add('hidden'); addForm.reset();
    (document.getElementById('entry-month') as HTMLInputElement).value = defaultMonth;
    (document.getElementById('entry-recurring') as HTMLInputElement).checked = false;
    (addForm.querySelector('.entry-type-btn[data-type="donation"]') as HTMLButtonElement).click();
    renderBudget();
  } else {
    const d = await r.json().catch(() => ({}));
    addErr.textContent = d.detail ?? 'Failed to add entry.'; addErr.classList.remove('hidden');
  }
});

// ── Feedback ──────────────────────────────────────────────────────────────────
const MAX_IMAGES = 3;
const MAX_BYTES  = 2 * 1024 * 1024;
const fbForm     = document.getElementById('feedback-form') as HTMLFormElement;
const fbDrop     = document.getElementById('fb-drop')!;
const fbInput    = document.getElementById('fb-input') as HTMLInputElement;
const fbPreviews = document.getElementById('fb-previews')!;
const fbErr      = document.getElementById('fb-error')!;
const fbSuccess  = document.getElementById('fb-success')!;
let fbFiles: File[] = [];

function fbShowErr(msg: string) { fbErr.textContent = msg; fbErr.classList.remove('hidden'); }
function fbClearErr() { fbErr.classList.add('hidden'); }
function fbRenderPreviews() {
  fbPreviews.innerHTML = '';
  fbFiles.forEach((f, i) => {
    const wrap = document.createElement('div');
    wrap.className = 'relative';
    wrap.innerHTML = `<img src="${URL.createObjectURL(f)}" class="w-20 h-20 object-cover rounded-lg border border-zinc-700" />
      <button type="button" data-i="${i}" class="rm-btn absolute -top-1.5 -right-1.5 w-5 h-5 rounded-full bg-zinc-800 border border-zinc-600 text-zinc-300 hover:text-white text-xs flex items-center justify-center">×</button>`;
    fbPreviews.appendChild(wrap);
  });
  fbPreviews.querySelectorAll<HTMLElement>('.rm-btn').forEach(b => {
    b.addEventListener('click', () => { fbFiles.splice(parseInt(b.dataset.i!), 1); fbRenderPreviews(); fbClearErr(); });
  });
}
function fbAddFiles(files: FileList | File[]) {
  fbClearErr();
  for (const f of Array.from(files)) {
    if (fbFiles.length >= MAX_IMAGES) { fbShowErr(`Max ${MAX_IMAGES} images.`); break; }
    if (f.size > MAX_BYTES) { fbShowErr(`"${f.name}" exceeds 2 MB.`); continue; }
    fbFiles.push(f);
  }
  fbRenderPreviews(); fbInput.value = '';
}
fbDrop.addEventListener('click', () => fbInput.click());
fbDrop.addEventListener('dragover', e => { e.preventDefault(); fbDrop.style.borderColor = 'var(--accent)'; });
fbDrop.addEventListener('dragleave', () => { fbDrop.style.borderColor = ''; });
fbDrop.addEventListener('drop', e => { e.preventDefault(); fbDrop.style.borderColor = ''; if (e.dataTransfer?.files.length) fbAddFiles(e.dataTransfer.files); });
fbInput.addEventListener('change', () => { if (fbInput.files?.length) fbAddFiles(fbInput.files); });
fbForm.addEventListener('submit', async e => {
  e.preventDefault(); fbClearErr();
  const text = (document.getElementById('fb-text') as HTMLTextAreaElement).value.trim();
  if (!text && fbFiles.length === 0) { fbShowErr('Please write something or attach an image.'); return; }
  const fd = new FormData(); fd.append('text', text);
  for (const f of fbFiles) fd.append('images', f);
  const btn = fbForm.querySelector('button[type=submit]') as HTMLButtonElement;
  btn.disabled = true; btn.textContent = 'Sending…';
  try {
    const r = await fetch('/api/feedback', { method: 'POST', credentials: 'include', body: fd });
    if (!r.ok) { const d = await r.json().catch(() => ({})); throw new Error(d.detail ?? `Error ${r.status}`); }
    fbForm.classList.add('hidden'); fbSuccess.classList.remove('hidden');
  } catch (err: any) { fbShowErr(err.message); btn.disabled = false; btn.textContent = 'Send feedback'; }
});
</script>
