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

const errorBox = document.getElementById('error');
const board = document.getElementById('board');
const customerSel = document.getElementById('board-customer');
const icons = { epic: '◆', story: '▣', task: '▢' };

let customers = [];
let tasks = [];
let atomizerUp = true;
const selected = new Set();

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

async function checkHealth() {
  const badge = document.getElementById('atomizer-health');
  try {
    const res = await api('GET', '/api/v1/service-health');
    const a = res.atomizer;
    atomizerUp = a.healthy && a.breaker !== 'open';
    badge.textContent = atomizerUp
      ? `atomizer healthy · ${a.latencyMs} ms`
      : `atomizer ${a.healthy ? 'breaker ' + a.breaker : 'DOWN'} — Subdivide/Extend disabled`;
    badge.style.color = atomizerUp ? 'var(--ok)' : 'var(--err)';
  } catch (e) {
    atomizerUp = false;
    badge.textContent = 'atomizer status unknown';
  }
  document.querySelectorAll('[data-needs-atomizer]').forEach((b) => {
    b.disabled = !atomizerUp;
    b.title = atomizerUp ? '' : 'Atomization Service is unavailable';
  });
}

async function load() {
  try {
    const cid = customerSel.value || '';
    const res = await api('GET', `/api/v1/consultant/board?customerId=${encodeURIComponent(cid)}`);
    customers = res.customers;
    tasks = res.tasks;
    if (!customerSel.options.length) {
      customerSel.replaceChildren(
        new Option('All customers', ''),
        ...customers.map((c) => new Option(c.name, c.id)),
      );
      customerSel.value = new URLSearchParams(location.search).get('customerId') || '';
      if (customerSel.value) return load();
    }
    render();
  } catch (e) { errorBox.textContent = e.message; }
}

function childrenOf(id) { return tasks.filter((t) => t.parentId === id); }

function render() {
  selected.forEach((id) => { if (!tasks.find((t) => t.id === id)) selected.delete(id); });
  updateBulkButton();
  const roots = tasks.filter((t) => !t.parentId);
  board.replaceChildren(...roots.map(renderRoot));
  if (!roots.length) {
    board.innerHTML = '<p class="muted">No tasks on the atomization board for this selection.</p>';
  }
  checkHealth();
}

function statusBadge(t) {
  const cls = { atomizing: 'warn', published: 'ok' }[t.status] || '';
  return `<span class="badge" style="${cls === 'ok' ? 'color:var(--ok)' : cls === 'warn' ? 'color:var(--warn)' : ''}">${esc(t.status)}</span>`;
}

function renderRoot(root) {
  const card = document.createElement('div');
  card.className = 'card' + (root.status === 'atomizing' ? ' shimmer' : '');
  const cust = customers.find((c) => c.id === root.customerId);
  const icon = root.external ? (icons[root.external.type] || '▢') : '▢';
  const orphan = root.external && root.external.orphaned
    ? '<span class="badge" style="color:var(--err)" title="No longer returned by the upstream system">orphaned</span>' : '';
  card.innerHTML = `
    <div class="spread">
      <h3>${icon} ${esc(root.title)} ${statusBadge(root)} ${orphan}</h3>
      <span class="muted">${esc(cust ? cust.name : '')} ${root.external ? '· ' + esc(root.external.key) : ''}</span>
    </div>
    <p class="muted">${esc((root.description || '').slice(0, 240))}</p>
    <div class="spread">
      <span>Budget <strong>${root.budget}</strong> · bounty preview <strong>${root.bounty}</strong></span>
      <span>
        ${root.external && root.external.url ? `<a class="btn small" href="${esc(root.external.url)}" target="_blank" rel="noopener">source ↗</a>` : ''}
        <button class="btn small" data-act="edit">Edit</button>
        <button class="btn small primary" data-act="subdivide" data-needs-atomizer ${root.status === 'atomizing' ? 'disabled' : ''}>Subdivide</button>
        <button class="btn small" data-act="extend" data-needs-atomizer>Extend</button>
        <button class="btn small" data-act="archive">Archive</button>
      </span>
    </div>
    <div class="children"></div>`;
  card.querySelector('[data-act=subdivide]').addEventListener('click', () => openSubdivide(root));
  card.querySelector('[data-act=extend]').addEventListener('click', () => openExtend(root));
  card.querySelector('[data-act=edit]').addEventListener('click', () => openEdit(root, true));
  card.querySelector('[data-act=archive]').addEventListener('click', () => archive(root));

  const kids = childrenOf(root.id);
  if (kids.length) {
    card.querySelector('.children').appendChild(renderChildren(root, kids, 1));
  }
  return card;
}

function renderChildren(parent, kids, depth) {
  const wrap = document.createElement('div');
  wrap.style.marginLeft = `${depth * 16}px`;
  wrap.className = 'stack mt';

  const subdivided = kids.filter((k) => k.origin === 'subdivided');
  const sum = subdivided.reduce((acc, k) => acc + k.effortCoefficient, 0);
  const hasExtension = kids.some((k) => k.origin === 'extended');
  if (subdivided.length) {
    const ind = document.createElement('p');
    const bad = !hasExtension && Math.abs(sum - 1) > 0.005;
    ind.innerHTML = `Coefficient sum: <strong style="color:${bad ? 'var(--err)' : 'var(--ok)'}">${sum.toFixed(2)}</strong>` +
      (hasExtension ? ' <span class="muted">(extensions allow > 1.00)</span>'
        : bad ? ' — should be 1.00' : '');
    wrap.appendChild(ind);
  }

  kids.forEach((k) => {
    const row = document.createElement('div');
    row.className = 'card' + (k.status === 'atomizing' ? ' shimmer' : '');
    row.style.padding = '12px 16px';
    const ext = k.origin === 'extended' ? '<span class="badge">extension</span>' : '';
    row.innerHTML = `
      <div class="spread">
        <span>
          ${k.status === 'atomized' ? `<input type="checkbox" data-sel aria-label="Select ${esc(k.title)} for publishing" ${selected.has(k.id) ? 'checked' : ''}>` : ''}
          <strong>${esc(k.title)}</strong> ${ext} ${statusBadge(k)}
        </span>
        <span>bounty <strong data-bounty>${k.bounty}</strong></span>
      </div>
      <p class="muted" style="margin:4px 0">${esc((k.description || '').slice(0, 160))}</p>
      <div class="spread">
        <label style="flex:1;font-weight:normal">
          <span class="muted">effort <span data-coeff>${k.effortCoefficient.toFixed(2)}</span></span>
          <input type="range" min="0.01" max="${k.origin === 'extended' ? 2 : 1}" step="0.01"
            value="${k.effortCoefficient}" ${['atomized', 'published'].includes(k.status) ? '' : 'disabled'}
            aria-label="Effort coefficient for ${esc(k.title)}">
        </label>
        <span>
          <button class="btn small" data-act="edit">Edit</button>
          ${['imported', 'atomized'].includes(k.status)
            ? '<button class="btn small" data-act="subdivide" data-needs-atomizer>Subdivide</button>' : ''}
          <button class="btn small" data-act="extend" data-needs-atomizer>Extend</button>
          ${k.status === 'atomized' ? '<button class="btn small primary" data-act="publish">Publish</button>' : ''}
          <button class="btn small" data-act="archive">Archive</button>
        </span>
      </div>`;
    const slider = row.querySelector('input[type=range]');
    let sliderTimer = null;
    slider.addEventListener('input', () => {
      row.querySelector('[data-coeff]').textContent = parseFloat(slider.value).toFixed(2);
      row.querySelector('[data-bounty]').textContent = (slider.value * k.budget).toFixed(2);
      clearTimeout(sliderTimer);
      sliderTimer = setTimeout(async () => {
        try {
          const res = await api('PATCH', `/api/v1/tasks/${k.id}`, {
            effortCoefficient: parseFloat(slider.value), version: k.version,
          });
          k.version = res.task.version;
          k.effortCoefficient = res.task.effortCoefficient;
          k.bounty = res.task.bounty;
          render();
        } catch (e) {
          if (e.status === 409) { toast('Task changed elsewhere — reloading.', 'err'); load(); }
          else toast(e.message, 'err');
        }
      }, 400);
    });
    const sel = row.querySelector('[data-sel]');
    if (sel) {
      sel.addEventListener('change', () => {
        if (sel.checked) selected.add(k.id); else selected.delete(k.id);
        updateBulkButton();
      });
    }
    row.querySelector('[data-act=edit]').addEventListener('click', () => openEdit(k, false));
    const sd = row.querySelector('[data-act=subdivide]');
    if (sd) sd.addEventListener('click', () => openSubdivide(k));
    row.querySelector('[data-act=extend]').addEventListener('click', () => openExtend(k));
    row.querySelector('[data-act=archive]').addEventListener('click', () => archive(k));
    const pub = row.querySelector('[data-act=publish]');
    if (pub) {
      pub.addEventListener('click', async () => {
        try { await api('POST', `/api/v1/tasks/${k.id}/publish`, {}); toast('Published.', 'ok'); load(); }
        catch (e) { toast(e.message, 'err'); }
      });
    }
    wrap.appendChild(row);
    const grand = childrenOf(k.id);
    if (grand.length) wrap.appendChild(renderChildren(k, grand, depth + 1));
  });
  return wrap;
}

async function archive(t) {
  if (!confirm(`Archive "${t.title}"?`)) return;
  try { await api('POST', `/api/v1/tasks/${t.id}/archive`, {}); load(); }
  catch (e) { toast(e.message, 'err'); }
}

function updateBulkButton() {
  const btn = document.getElementById('bulk-publish');
  btn.textContent = `Publish selected (${selected.size})`;
  btn.disabled = selected.size === 0;
}

document.getElementById('select-all').addEventListener('click', () => {
  tasks.filter((t) => t.status === 'atomized').forEach((t) => selected.add(t.id));
  render();
});

document.getElementById('bulk-publish').addEventListener('click', async () => {
  try {
    const res = await api('POST', '/api/v1/tasks/publish', { ids: [...selected] });
    toast(`Published ${res.published.length} task(s)` +
      (Object.keys(res.failed).length ? `, ${Object.keys(res.failed).length} failed` : '.'),
      Object.keys(res.failed).length ? 'err' : 'ok');
    selected.clear();
    load();
  } catch (e) { toast(e.message, 'err'); }
});

// ---- dialogs ----
let dialogTask = null;

const sdDlg = document.getElementById('subdivide-dialog');
function openSubdivide(t) {
  dialogTask = t;
  document.getElementById('subdivide-task-title').textContent = t.title;
  document.getElementById('sd-replace-warning').hidden = !childrenOf(t.id).length;
  sdDlg.showModal();
}
document.getElementById('sd-cancel').addEventListener('click', () => sdDlg.close());
document.getElementById('subdivide-form').addEventListener('submit', async (e) => {
  e.preventDefault();
  try {
    await api('POST', `/api/v1/tasks/${dialogTask.id}/subdivide`, {
      note: document.getElementById('sd-note').value.trim(),
      constraints: {
        minTasks: parseInt(document.getElementById('sd-min').value, 10) || 0,
        maxTasks: parseInt(document.getElementById('sd-max').value, 10) || 0,
      },
      confirmReplace: !document.getElementById('sd-replace-warning').hidden,
    });
    sdDlg.close();
    toast('Subdivision queued — results stream in shortly.', 'ok');
    load();
  } catch (err) { toast(err.message, 'err'); }
});

const exDlg = document.getElementById('extend-dialog');
function openExtend(t) {
  dialogTask = t;
  document.getElementById('extend-task-title').textContent = t.title;
  exDlg.showModal();
}
document.getElementById('ex-cancel').addEventListener('click', () => exDlg.close());
document.getElementById('extend-form').addEventListener('submit', async (e) => {
  e.preventDefault();
  try {
    await api('POST', `/api/v1/tasks/${dialogTask.id}/extend`, {
      note: document.getElementById('ex-note').value.trim(),
    });
    exDlg.close();
    document.getElementById('ex-note').value = '';
    toast('Extension queued.', 'ok');
  } catch (err) { toast(err.message, 'err'); }
});

const edDlg = document.getElementById('edit-dialog');
function openEdit(t, isRoot) {
  dialogTask = t;
  document.getElementById('ed-title').value = t.title;
  document.getElementById('ed-desc').value = t.description || '';
  document.getElementById('ed-ac').value = (t.acceptanceCriteria || []).join('\n');
  document.getElementById('ed-budget').value = t.budget;
  document.getElementById('ed-cascade-wrap').style.display = isRoot ? '' : 'none';
  edDlg.showModal();
}
document.getElementById('ed-cancel').addEventListener('click', () => edDlg.close());
document.getElementById('edit-form').addEventListener('submit', async (e) => {
  e.preventDefault();
  try {
    await api('PATCH', `/api/v1/tasks/${dialogTask.id}`, {
      version: dialogTask.version,
      title: document.getElementById('ed-title').value.trim(),
      description: document.getElementById('ed-desc').value,
      acceptanceCriteria: document.getElementById('ed-ac').value.split('\n').map((s) => s.trim()).filter(Boolean),
      budget: parseFloat(document.getElementById('ed-budget').value) || 0,
      cascadeBudget: document.getElementById('ed-cascade').checked &&
        document.getElementById('ed-cascade-wrap').style.display !== 'none',
    });
    edDlg.close();
    toast('Saved.', 'ok');
    load();
  } catch (err) {
    if (err.status === 409) { toast('Task changed elsewhere — reloading.', 'err'); load(); }
    else toast(err.message, 'err');
  }
});

// ---- live updates ----
let reloadTimer = null;
function scheduleReload() {
  clearTimeout(reloadTimer);
  reloadTimer = setTimeout(load, 300);
}
subscribe('board', () => scheduleReload());
onPollFallback(load);
customerSel.addEventListener('change', load);
setInterval(checkHealth, 30000);
load();
