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

const root = document.getElementById('task-root');
const errorBox = document.getElementById('error');
const taskId = root.dataset.taskId;
const meId = root.dataset.userId;
const isConsultant = root.dataset.isConsultant === '1' || root.dataset.isAdmin === '1';

let task = null;
const userCache = new Map();

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

async function userName(id) {
  if (!id || id.startsWith('system')) return id || 'system';
  if (!userCache.has(id)) {
    try {
      const res = await api('GET', `/api/v1/users/${id}/card`);
      userCache.set(id, res.card.name);
    } catch (e) { userCache.set(id, id.slice(0, 8)); }
  }
  return userCache.get(id);
}

async function load() {
  try {
    const res = await api('GET', `/api/v1/tasks/${taskId}`);
    task = res.task;
    await render();
  } catch (e) {
    errorBox.textContent = e.message;
    root.innerHTML = '';
  }
}

const mine = () => task.assignee && task.assignee.kind === 'human' && task.assignee.userId === meId;
const myClaim = () => (task.claimRequests || []).some((c) => c.developerId === meId);

async function render() {
  const t = task;
  const assigneeName = t.assignee
    ? (t.assignee.kind === 'ai' ? `AI (job ${esc(t.assignee.jobId || '')})` : esc(await userName(t.assignee.userId)))
    : '—';
  const orphan = t.external && t.external.orphaned
    ? '<span class="badge" style="color:var(--err)">orphaned upstream</span>' : '';

  const claims = await Promise.all((t.claimRequests || []).map(async (c) => `
    <li>${esc(await userName(c.developerId))}${c.note ? ' — ' + esc(c.note) : ''}
      ${isConsultant ? `
        <button class="btn small primary" data-approve="${esc(c.developerId)}">Approve</button>
        <button class="btn small" data-decline="${esc(c.developerId)}">Decline</button>` : ''}
    </li>`));

  const comments = await Promise.all((t.comments || []).map(async (c) => `
    <div class="card" style="padding:12px">
      <p class="muted" style="margin:0 0 8px">${esc(await userName(c.authorId))} · ${new Date(c.at).toLocaleString()}</p>
      <div>${c.body}</div>
    </div>`));

  const timeline = await Promise.all((t.timeline || []).slice().reverse().map(async (e) => `
    <tr><td>${new Date(e.at).toLocaleString()}</td>
        <td>${esc(await userName(e.actorId))}</td>
        <td>${esc(e.event)}${e.data && e.data.note ? ' — ' + esc(e.data.note) : ''}</td></tr>`));

  const timeRows = (t.timeLog || []).map((e) =>
    `<tr><td>${new Date(e.at).toLocaleString()}</td><td>${e.minutes} min</td><td>${esc(e.note)}</td></tr>`).join('');
  const totalMin = (t.timeLog || []).reduce((a, e) => a + e.minutes, 0);

  root.innerHTML = `
    <div class="spread">
      <h1>${esc(t.title)}</h1>
      <span class="badge accent" style="font-size:1.1rem">◈ ${t.bounty}</span>
    </div>
    <p class="muted">
      <span class="badge">${esc(t.status)}</span> ${orphan}
      ${t.external ? `· <a href="${esc(t.external.url)}" target="_blank" rel="noopener">${esc(t.external.key)} ↗</a>` : ''}
      · assignee: ${assigneeName}
      ${totalMin ? `· time logged: ${Math.floor(totalMin / 60)}h ${totalMin % 60}m` : ''}
    </p>
    <div id="actions" class="mt"></div>
    <div class="card mt">
      <h2>Description</h2>
      <p style="white-space:pre-wrap">${esc(t.description)}</p>
      ${(t.acceptanceCriteria || []).length ? `
        <h3>Acceptance criteria</h3>
        <ul>${t.acceptanceCriteria.map((c) => `<li>${esc(c)}</li>`).join('')}</ul>` : ''}
      ${(t.attachments || []).length ? `
        <h3>Attachments</h3>
        <ul>${t.attachments.map((a) => `<li><a href="${a.fileId ? '/files/' + a.fileId : esc(a.url)}" target="_blank" rel="noopener">${esc(a.name)}</a></li>`).join('')}</ul>` : ''}
      ${(t.links || []).length ? `
        <h3>Links</h3>
        <ul>${t.links.map((l) => `<li><a href="${esc(l)}" target="_blank" rel="noopener">${esc(l)}</a></li>`).join('')}</ul>` : ''}
    </div>
    ${(t.claimRequests || []).length ? `
    <div class="card mt"><h2>Assignment requests</h2><ul>${claims.join('')}</ul></div>` : ''}
    <div class="card mt">
      <h2>Comments</h2>
      <div class="stack">${comments.join('') || '<p class="muted">No comments yet.</p>'}</div>
      <form id="comment-form" class="mt">
        <div class="field">
          <label for="comment-body">Add comment (mention with @name or @email)</label>
          <textarea id="comment-body" required></textarea>
        </div>
        <button class="btn primary" type="submit">Comment</button>
      </form>
    </div>
    ${mine() ? `
    <div class="card mt">
      <h2>Log time</h2>
      <form id="time-form" class="row">
        <div class="field"><label for="time-minutes">Minutes</label>
          <input type="number" id="time-minutes" min="1" max="1440" required></div>
        <div class="field"><label for="time-note">Note</label>
          <input type="text" id="time-note"></div>
        <div class="field" style="flex:0;align-self:flex-end">
          <button class="btn primary" type="submit">Log</button></div>
      </form>
      ${timeRows ? `<table class="list"><thead><tr><th>When</th><th>Time</th><th>Note</th></tr></thead><tbody>${timeRows}</tbody></table>` : ''}
    </div>` : ''}
    <div class="card mt">
      <h2>Timeline</h2>
      <table class="list"><thead><tr><th>When</th><th>Who</th><th>Event</th></tr></thead>
      <tbody>${timeline.join('')}</tbody></table>
    </div>`;

  renderActions();
  wireHandlers();
}

function renderActions() {
  const host = document.getElementById('actions');
  const t = task;
  const add = (label, fn, primary) => {
    const b = document.createElement('button');
    b.className = 'btn' + (primary ? ' primary' : '');
    b.style.marginRight = '8px';
    b.textContent = label;
    b.addEventListener('click', fn);
    host.appendChild(b);
  };
  const post = (path, body = {}) => async () => {
    try { await api('POST', `/api/v1/tasks/${taskId}/${path}`, body); load(); }
    catch (e) { toast(e.message, 'err'); }
  };

  if (root.dataset.isDeveloper === '1') {
    if (t.status === 'published' && !myClaim()) add('Request assignment', post('claim', { note: '' }), true);
    if (myClaim() && ['published', 'claim_requested'].includes(t.status)) add('Withdraw request', post('claim/withdraw'));
    if (mine() && ['assigned', 'changes_requested'].includes(t.status)) add('Start work', post('start'), true);
    if (mine() && t.status === 'in_progress') add('Submit for review', post('submit-review'), true);
    if (mine() && ['assigned', 'in_progress'].includes(t.status)) add('Abandon', post('abandon'));
  }
  if (isConsultant) {
    if (t.status === 'in_review') {
      add('Review…', openReview, true);
    }
    if (t.status === 'published') add('Assign to AI…', () => document.getElementById('ai-dialog').showModal(), false);
    if (['assigned', 'in_progress'].includes(t.status)) add('Unassign', post('unassign'));
    if (t.status !== 'archived' && t.status !== 'approved') add('Archive', post('archive'));
  }
}

function wireHandlers() {
  document.querySelectorAll('[data-approve]').forEach((b) => {
    b.addEventListener('click', async () => {
      try {
        await api('POST', `/api/v1/tasks/${taskId}/approve-claim`, { developerId: b.dataset.approve });
        load();
      } catch (e) { toast(e.message, 'err'); }
    });
  });
  document.querySelectorAll('[data-decline]').forEach((b) => {
    b.addEventListener('click', async () => {
      try {
        await api('POST', `/api/v1/tasks/${taskId}/decline-claim`, { developerId: b.dataset.decline });
        load();
      } catch (e) { toast(e.message, 'err'); }
    });
  });
  document.getElementById('comment-form').addEventListener('submit', async (e) => {
    e.preventDefault();
    const body = document.getElementById('comment-body').value;
    try {
      await api('POST', `/api/v1/tasks/${taskId}/comments`, { body: '<p>' + esc(body).replace(/\n/g, '<br>') + '</p>' });
      load();
    } catch (err) { toast(err.message, 'err'); }
  });
  const timeForm = document.getElementById('time-form');
  if (timeForm) {
    timeForm.addEventListener('submit', async (e) => {
      e.preventDefault();
      try {
        await api('POST', `/api/v1/tasks/${taskId}/time`, {
          minutes: parseInt(document.getElementById('time-minutes').value, 10),
          note: document.getElementById('time-note').value.trim(),
        });
        load();
      } catch (err) { toast(err.message, 'err'); }
    });
  }
}

// ---- review dialog with per-AC checklist (§11.18) ----
const reviewDlg = document.getElementById('review-dialog');
function openReview() {
  const host = document.getElementById('review-checklist');
  host.replaceChildren(...(task.acceptanceCriteria || []).map((c, i) => {
    const div = document.createElement('div');
    div.innerHTML = `<label style="font-weight:normal">
      <input type="checkbox" data-ac="${i}" checked> ${esc(c)}</label>`;
    return div;
  }));
  reviewDlg.showModal();
}
document.getElementById('review-cancel').addEventListener('click', () => reviewDlg.close());

async function submitReview(decision) {
  const checklist = [...document.querySelectorAll('[data-ac]')].map((cb, i) => ({
    criterion: task.acceptanceCriteria[i], ok: cb.checked,
  }));
  try {
    await api('POST', `/api/v1/tasks/${taskId}/review`, {
      decision, note: document.getElementById('review-note').value.trim(), checklist,
    });
    reviewDlg.close();
    toast(decision === 'approve' ? 'Approved — bounty awarded.' : 'Changes requested.', 'ok');
    load();
  } catch (e) { toast(e.message, 'err'); }
}
document.getElementById('review-approve').addEventListener('click', () => submitReview('approve'));
document.getElementById('review-changes').addEventListener('click', () => submitReview('request_changes'));

// ---- AI dialog ----
const aiDlg = document.getElementById('ai-dialog');
document.getElementById('ai-cancel').addEventListener('click', () => aiDlg.close());
document.getElementById('ai-form').addEventListener('submit', async (e) => {
  e.preventDefault();
  try {
    await api('POST', `/api/v1/tasks/${taskId}/assign-ai`, {
      context: {
        repositoryUrl: document.getElementById('ai-repo').value.trim(),
        branch: document.getElementById('ai-branch').value.trim(),
        instructions: document.getElementById('ai-instructions').value.trim(),
      },
    });
    aiDlg.close();
    toast('AI job created — the result will arrive for review.', 'ok');
    load();
  } catch (err) { toast(err.message, 'err'); }
});

subscribe('board', (event, data) => {
  if (data && data.taskId === taskId) setTimeout(load, 200);
});
load();
