import { api, toast } from '/static/js/api.js';
import { subscribe, onPollFallback } from '/static/js/ws.js';

const grid = document.getElementById('board-grid');
const errorBox = document.getElementById('error');
let meId = '';
let tasks = [];
const customerNames = new Map();

const esc = (s) => String(s ?? '').replace(/[&<>"']/g, (c) => ({
  '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;',
}[c]));

function filters() {
  return {
    q: document.getElementById('f-q').value.trim(),
    customerId: document.getElementById('f-customer').value,
    minBounty: document.getElementById('f-min').value || '0',
    sort: document.getElementById('f-sort').value,
  };
}

async function load() {
  try {
    const f = filters();
    const qs = new URLSearchParams(f).toString();
    const res = await api('GET', '/api/v1/board?' + qs);
    tasks = res.tasks;
    (res.customers || []).forEach((c) => customerNames.set(c.id, c.name));
    const sel = document.getElementById('f-customer');
    if (sel.options.length === 1) {
      res.customers.forEach((c) => sel.add(new Option(c.name, c.id)));
    }
    render();
  } catch (e) { errorBox.textContent = e.message; }
}

function ageBadge(t) {
  if (!t.publishedAt) return '';
  const days = (Date.now() - new Date(t.publishedAt).getTime()) / 86400000;
  if (days > 14) return '<span class="badge" style="color:var(--err)" title="Published more than 14 days ago">14d+</span>';
  if (days > 7) return '<span class="badge" style="color:var(--warn)" title="Published more than 7 days ago">7d+</span>';
  return '';
}

function render() {
  grid.replaceChildren(...tasks.map(renderCard));
  if (!tasks.length) {
    grid.innerHTML = '<p class="muted">No published tasks right now. Consultants add you to their pool to share work here.</p>';
  }
}

function renderCard(t) {
  const card = document.createElement('div');
  card.className = 'card';
  const myClaim = (t.claimRequests || []).some((c) => c.developerId === meId);
  const others = (t.claimRequests || []).filter((c) => c.developerId !== meId).length;
  card.innerHTML = `
    <div class="spread">
      <span class="badge accent" style="font-size:1rem">◈ ${t.bounty}</span>
      <span>${ageBadge(t)}</span>
    </div>
    ${customerNames.has(t.customerId) ? `<p class="muted card-project">${esc(customerNames.get(t.customerId))}</p>` : ''}
    <h3 class="mt"><a href="/tasks/${t.id}">${esc(t.title)}</a></h3>
    <p class="muted">${esc((t.description || '').slice(0, 180))}</p>
    <p class="muted">${(t.acceptanceCriteria || []).length} acceptance criteria</p>
    <div class="spread">
      <span class="muted">
        ${myClaim ? '<span class="badge" style="color:var(--ok)">requested by you</span>' : ''}
        ${others ? `<span class="badge">${others} other request(s)</span>` : ''}
      </span>
      ${myClaim
        ? '<button class="btn small" data-act="withdraw">Withdraw</button>'
        : '<button class="btn primary small" data-act="claim">Request assignment</button>'}
    </div>`;
  const claimBtn = card.querySelector('[data-act=claim]');
  if (claimBtn) claimBtn.addEventListener('click', () => openClaim(t));
  const wBtn = card.querySelector('[data-act=withdraw]');
  if (wBtn) {
    wBtn.addEventListener('click', async () => {
      try { await api('POST', `/api/v1/tasks/${t.id}/claim/withdraw`, {}); load(); }
      catch (e) { toast(e.message, 'err'); }
    });
  }
  return card;
}

const dlg = document.getElementById('claim-dialog');
let claimTask = null;
function openClaim(t) {
  claimTask = t;
  document.getElementById('claim-task-title').textContent = `${t.title} — bounty ${t.bounty}`;
  dlg.showModal();
}
document.getElementById('claim-cancel').addEventListener('click', () => dlg.close());
document.getElementById('claim-form').addEventListener('submit', async (e) => {
  e.preventDefault();
  try {
    await api('POST', `/api/v1/tasks/${claimTask.id}/claim`, {
      note: document.getElementById('claim-note').value.trim(),
    });
    dlg.close();
    document.getElementById('claim-note').value = '';
    toast('Assignment requested — the consultant will review it.', 'ok');
    load();
  } catch (err) { toast(err.message, 'err'); }
});

// saved filters (§11.8)
document.getElementById('f-save').addEventListener('click', async () => {
  try {
    const me = await api('GET', '/api/v1/auth/me');
    const extra = me.user.extra || {};
    extra.savedBoardFilters = JSON.stringify(filters());
    await api('PATCH', '/api/v1/profile', { extra });
    toast('Filters saved as your default.', 'ok');
  } catch (e) { toast(e.message, 'err'); }
});

async function restoreFilters() {
  try {
    const me = await api('GET', '/api/v1/auth/me');
    meId = me.user.id;
    const saved = me.user.extra && me.user.extra.savedBoardFilters;
    if (saved) {
      const f = JSON.parse(saved);
      document.getElementById('f-q').value = f.q || '';
      document.getElementById('f-min').value = f.minBounty > 0 ? f.minBounty : '';
      document.getElementById('f-sort').value = f.sort || '';
    }
  } catch (e) { /* anonymous — page guard handles it */ }
}

let timer = null;
['f-q', 'f-min'].forEach((id) => {
  document.getElementById(id).addEventListener('input', () => {
    clearTimeout(timer);
    timer = setTimeout(load, 300);
  });
});
['f-customer', 'f-sort'].forEach((id) => {
  document.getElementById(id).addEventListener('change', load);
});

subscribe('board', () => { clearTimeout(timer); timer = setTimeout(load, 400); });
onPollFallback(load);
restoreFilters().then(load);
