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

const errorBox = document.getElementById('error');
const showErr = (e) => { errorBox.textContent = e.message || String(e); };

// ---- tabs ----
const tabs = document.querySelectorAll('[role=tab]');
tabs.forEach((btn) => btn.addEventListener('click', () => {
  tabs.forEach((b) => b.setAttribute('aria-selected', b === btn ? 'true' : 'false'));
  document.querySelectorAll('[role=tabpanel]').forEach((p) => { p.hidden = true; });
  document.getElementById('tab-' + btn.dataset.tab).hidden = false;
  loaders[btn.dataset.tab]();
}));

// ---- customers ----
const dlg = document.getElementById('customer-dialog');
let editingCustomer = null; // null = creating
let consultants = [];

function credsFromForm(type) {
  switch (type) {
    case 'jira':
      return { email: val('c-jira-email'), apiToken: val('c-jira-token') };
    case 'azure_devops':
      return { organization: val('c-ado-org'), project: val('c-ado-project'), pat: val('c-ado-pat') };
    case 'youtrack':
      return { permanentToken: val('c-yt-token') };
    case 'wekan':
      return { username: val('c-wekan-user'), password: val('c-wekan-pass') };
    default:
      return {};
  }
}
const val = (id) => document.getElementById(id).value.trim();

function credsEntered(type) {
  const c = credsFromForm(type);
  return Object.values(c).some(Boolean);
}

document.getElementById('c-type').addEventListener('change', (e) => {
  document.querySelectorAll('.creds').forEach((f) => { f.hidden = true; });
  document.getElementById('creds-' + e.target.value).hidden = false;
  document.getElementById('f-baseurl').style.display =
    e.target.value === 'demo' ? 'none' : '';
});

async function loadConsultants() {
  const res = await api('GET', '/api/v1/admin/users?role=consultant&limit=200');
  consultants = res.users;
  const sel = document.getElementById('c-consultants');
  sel.replaceChildren(...consultants.map((u) => {
    const o = document.createElement('option');
    o.value = u.id;
    o.textContent = `${u.name} <${u.email}>`;
    return o;
  }));
}

// Render one username input per selected consultant, prefilled from the
// customer's saved per-consultant ticketing identity map.
function renderIdentities() {
  const wrap = document.getElementById('c-identities-wrap');
  const host = document.getElementById('c-identities');
  const selectedIds = [...document.getElementById('c-consultants').selectedOptions].map((o) => o.value);
  const existing = (editingCustomer && editingCustomer.ticketing.consultantIdentities) || {};
  if (!selectedIds.length) { wrap.hidden = true; host.replaceChildren(); return; }
  wrap.hidden = false;
  host.replaceChildren(...selectedIds.map((id) => {
    const u = consultants.find((c) => c.id === id) || { name: id, email: '' };
    const row = document.createElement('div');
    row.className = 'spread';
    row.style.marginTop = '6px';
    const label = document.createElement('span');
    label.className = 'muted';
    label.style.flex = '1';
    label.textContent = u.name;
    const inp = document.createElement('input');
    inp.type = 'text';
    inp.dataset.identity = id;
    inp.placeholder = 'username in ticketing system';
    inp.value = existing[id] || '';
    inp.style.flex = '1';
    row.append(label, inp);
    return row;
  }));
}
document.getElementById('c-consultants').addEventListener('change', renderIdentities);

function openCustomerDialog(customer) {
  editingCustomer = customer || null;
  document.getElementById('customer-dialog-title').textContent =
    customer ? `Edit ${customer.name}` : 'New customer';
  document.getElementById('c-name').value = customer ? customer.name : '';
  document.getElementById('c-type').value = customer ? customer.ticketing.type : 'jira';
  document.getElementById('c-project').value = customer ? customer.ticketing.projectKey : '';
  document.getElementById('c-baseurl').value = customer ? customer.ticketing.baseUrl : '';
  document.getElementById('c-poll').value = customer ? customer.ticketing.pollIntervalSec : 60;
  document.getElementById('c-budget').value = customer ? customer.defaultBudget : 1000;
  document.querySelectorAll('.creds input').forEach((i) => { i.value = ''; });
  document.getElementById('c-type').dispatchEvent(new Event('change'));
  const sel = document.getElementById('c-consultants');
  [...sel.options].forEach((o) => {
    o.selected = customer ? customer.consultantIds.includes(o.value) : false;
  });
  renderIdentities();
  document.getElementById('c-test-result').textContent =
    customer && customer.ticketing.hasCredentials ? 'Stored credentials are kept unless you enter new ones.' : '';
  dlg.showModal();
}

document.getElementById('customer-new').addEventListener('click', async () => {
  try { await loadConsultants(); openCustomerDialog(null); } catch (e) { showErr(e); }
});
document.getElementById('c-cancel').addEventListener('click', () => dlg.close());

document.getElementById('c-test').addEventListener('click', async () => {
  const out = document.getElementById('c-test-result');
  out.textContent = 'Testing…';
  const type = val('c-type');
  try {
    let res;
    if (editingCustomer && !credsEntered(type)) {
      res = await api('POST', `/api/v1/admin/customers/${editingCustomer.id}/test-connection`, {});
    } else {
      res = await api('POST', '/api/v1/admin/customers/test-connection', {
        type, baseUrl: val('c-baseurl'), projectKey: val('c-project'),
        credentials: credsFromForm(type),
      });
    }
    out.textContent = res.ok ? `✓ Connected (${res.latencyMs} ms)` : `✗ ${res.error}`;
    out.style.color = res.ok ? 'var(--ok)' : 'var(--err)';
  } catch (e) { out.textContent = '✗ ' + e.message; out.style.color = 'var(--err)'; }
});

document.getElementById('customer-form').addEventListener('submit', async (e) => {
  e.preventDefault();
  const type = val('c-type');
  const body = {
    name: val('c-name'),
    type,
    baseUrl: val('c-baseurl'),
    projectKey: val('c-project'),
    pollIntervalSec: parseInt(val('c-poll'), 10) || 60,
    defaultBudget: parseFloat(val('c-budget')) || 0,
    consultantIds: [...document.getElementById('c-consultants').selectedOptions].map((o) => o.value),
  };
  const identities = {};
  document.querySelectorAll('#c-identities input[data-identity]').forEach((inp) => {
    if (inp.value.trim()) identities[inp.dataset.identity] = inp.value.trim();
  });
  body.consultantIdentities = identities;
  if (!editingCustomer || credsEntered(type)) body.credentials = credsFromForm(type);
  try {
    if (editingCustomer) {
      body.version = editingCustomer.version;
      await api('PATCH', `/api/v1/admin/customers/${editingCustomer.id}`, body);
    } else {
      await api('POST', '/api/v1/admin/customers', body);
    }
    dlg.close();
    toast('Customer saved.', 'ok');
    loadCustomers();
  } catch (err) {
    if (err.status === 409) toast('Conflict: ' + err.message, 'err');
    else showErr(err);
  }
});

async function loadCustomers() {
  try {
    const res = await api('GET', '/api/v1/admin/customers?includeArchived=true');
    const host = document.getElementById('customer-list');
    host.replaceChildren(...res.customers.map(renderCustomerCard));
    if (!res.customers.length) {
      host.textContent = 'No customers yet — create the first one.';
    }
  } catch (e) { showErr(e); }
}

function renderCustomerCard(c) {
  const card = document.createElement('div');
  card.className = 'card';
  const syncBadge = c.ticketing.lastSyncStatus === 'ok'
    ? `<span class="badge" style="color:var(--ok)">sync ok</span>`
    : c.ticketing.lastSyncStatus === 'error'
      ? `<span class="badge" style="color:var(--err)" title="${esc(c.ticketing.lastSyncError)}">sync error</span>`
      : '<span class="badge">never synced</span>';
  card.innerHTML = `
    <div class="spread">
      <h3>${esc(c.name)} ${c.archived ? '<span class="badge">archived</span>' : ''}</h3>
      <span>${syncBadge}</span>
    </div>
    <p class="muted">${esc(c.ticketing.type)} · project ${esc(c.ticketing.projectKey || '—')} ·
      poll ${c.ticketing.pollIntervalSec}s · budget ${c.defaultBudget} ·
      ${c.consultantIds.length} consultant(s)
      ${c.ticketing.lastSyncAt && !c.ticketing.lastSyncAt.startsWith('0001') ? ' · last sync ' + new Date(c.ticketing.lastSyncAt).toLocaleString() : ''}</p>
    <div>
      <button class="btn small" data-act="edit">Edit</button>
      <button class="btn small" data-act="sync">Sync now</button>
      <button class="btn small" data-act="archive">${c.archived ? 'Unarchive' : 'Archive'}</button>
      <button class="btn small danger" data-act="delete">Delete</button>
    </div>`;
  card.querySelector('[data-act=edit]').addEventListener('click', async () => {
    try { await loadConsultants(); openCustomerDialog(c); } catch (e) { showErr(e); }
  });
  card.querySelector('[data-act=sync]').addEventListener('click', async () => {
    try { await api('POST', `/api/v1/admin/customers/${c.id}/sync-now`, {}); toast('Sync queued.', 'ok'); }
    catch (e) { toast(e.message, 'err'); }
  });
  card.querySelector('[data-act=archive]').addEventListener('click', async () => {
    try {
      await api('PATCH', `/api/v1/admin/customers/${c.id}`, { archived: !c.archived, version: c.version });
      loadCustomers();
    } catch (e) { toast(e.message, 'err'); }
  });
  card.querySelector('[data-act=delete]').addEventListener('click', async () => {
    if (!confirm(`Delete customer "${c.name}"? This cannot be undone.`)) return;
    try { await api('DELETE', `/api/v1/admin/customers/${c.id}`); loadCustomers(); }
    catch (e) { toast(e.message, 'err'); }
  });
  return card;
}

// ---- users ----
let userSearchTimer;
document.getElementById('user-search').addEventListener('input', () => {
  clearTimeout(userSearchTimer);
  userSearchTimer = setTimeout(loadUsers, 250);
});

async function loadUsers() {
  try {
    const q = encodeURIComponent(document.getElementById('user-search').value.trim());
    const res = await api('GET', `/api/v1/admin/users?q=${q}&limit=200`);
    const tbody = document.querySelector('#user-table tbody');
    tbody.replaceChildren(...res.users.map(renderUserRow));
  } catch (e) { showErr(e); }
}

function renderUserRow(u) {
  const tr = document.createElement('tr');
  const roleCell = (role) => {
    const td = document.createElement('td');
    const cb = document.createElement('input');
    cb.type = 'checkbox';
    cb.checked = u.roles[role];
    cb.setAttribute('aria-label', `${role} role for ${u.name}`);
    cb.addEventListener('change', async () => {
      const roles = { ...u.roles, [role]: cb.checked };
      try {
        const res = await api('PATCH', `/api/v1/admin/users/${u.id}`, { roles });
        u = res.user;
        toast(`Roles updated for ${u.name}.`, 'ok');
      } catch (e) { cb.checked = !cb.checked; toast(e.message, 'err'); }
    });
    td.appendChild(cb);
    return td;
  };
  const name = document.createElement('td'); name.textContent = u.name;
  const email = document.createElement('td'); email.textContent = u.email;
  const status = document.createElement('td');
  status.textContent = u.disabled ? 'disabled' : 'active';
  const actions = document.createElement('td');

  const disableBtn = document.createElement('button');
  disableBtn.className = 'btn small';
  disableBtn.textContent = u.disabled ? 'Enable' : 'Disable';
  disableBtn.addEventListener('click', async () => {
    try {
      const res = await api('PATCH', `/api/v1/admin/users/${u.id}`, { disabled: !u.disabled });
      u = res.user; status.textContent = u.disabled ? 'disabled' : 'active';
      disableBtn.textContent = u.disabled ? 'Enable' : 'Disable';
    } catch (e) { toast(e.message, 'err'); }
  });
  const resetBtn = document.createElement('button');
  resetBtn.className = 'btn small';
  resetBtn.textContent = 'Force reset';
  resetBtn.addEventListener('click', async () => {
    try { await api('PATCH', `/api/v1/admin/users/${u.id}`, { forceReset: true }); toast('Password reset forced.', 'ok'); }
    catch (e) { toast(e.message, 'err'); }
  });
  const delBtn = document.createElement('button');
  delBtn.className = 'btn small danger';
  delBtn.textContent = 'Delete';
  delBtn.addEventListener('click', async () => {
    if (!confirm(`Delete user ${u.email}?`)) return;
    try { await api('DELETE', `/api/v1/admin/users/${u.id}`); tr.remove(); }
    catch (e) { toast(e.message, 'err'); }
  });
  actions.append(disableBtn, document.createTextNode(' '), resetBtn, document.createTextNode(' '), delBtn);
  tr.append(name, email, roleCell('admin'), roleCell('consultant'), roleCell('developer'), status, actions);
  return tr;
}

// ---- settings ----
async function loadSettings() {
  try {
    const res = await api('GET', '/api/v1/admin/settings');
    const s = res.settings;
    document.getElementById('s-branding').value = s.brandingName;
    document.getElementById('s-atomizer').value = s.atomizerBaseUrlOverride;
    document.getElementById('s-poll').value = s.defaultPollIntervalSec;
    document.getElementById('s-budget').value = s.defaultCustomerBudget;
    document.getElementById('s-hideclaims').checked = s.hideCompetingClaims;
  } catch (e) { showErr(e); }
}

document.getElementById('settings-form').addEventListener('submit', async (e) => {
  e.preventDefault();
  try {
    await api('PATCH', '/api/v1/admin/settings', {
      brandingName: val('s-branding'),
      atomizerBaseUrlOverride: val('s-atomizer'),
      defaultPollIntervalSec: parseInt(val('s-poll'), 10),
      defaultCustomerBudget: parseFloat(val('s-budget')),
      hideCompetingClaims: document.getElementById('s-hideclaims').checked,
    });
    toast('Settings saved.', 'ok');
  } catch (err) { toast(err.message, 'err'); }
});

// ---- status ----
async function loadStatus() {
  const host = document.getElementById('status-body');
  host.textContent = 'Loading…';
  try {
    const s = await api('GET', '/api/v1/admin/service-status');
    const ext = (e) => `
      <div class="spread">
        <span><strong>${esc(e.name)}</strong> <span class="muted">${esc(e.url)}</span></span>
        <span>${e.healthy
          ? `<span class="badge" style="color:var(--ok)">healthy · ${e.latencyMs} ms</span>`
          : `<span class="badge" style="color:var(--err)" title="${esc(e.error || '')}">down</span>`}
          <span class="badge">breaker: ${esc(e.breaker)}</span></span>
      </div>`;
    let workers = '<p class="muted">No sync workers running.</p>';
    if (Array.isArray(s.syncWorkers) && s.syncWorkers.length) {
      workers = s.syncWorkers.map((wkr) => `
        <div class="spread">
          <span>${esc(wkr.customerName)} <span class="muted">every ${wkr.pollIntervalSec}s</span></span>
          <span class="badge" style="color:${wkr.lastStatus === 'error' ? 'var(--err)' : 'var(--ok)'}"
            title="${esc(wkr.lastError || '')}">${esc(wkr.lastStatus || 'pending')}</span>
        </div>`).join('');
    }
    host.innerHTML = `
      <div class="spread"><strong>MongoDB</strong>
        ${s.mongo.healthy ? '<span class="badge" style="color:var(--ok)">healthy</span>'
          : `<span class="badge" style="color:var(--err)">${esc(s.mongo.error)}</span>`}</div>
      ${ext(s.atomizer)}
      ${ext(s.workPerformer)}
      <h3>Sync workers</h3>${workers}
      <h3>Job queue</h3>
      <p class="muted">${esc(JSON.stringify(s.jobs))}</p>`;
  } catch (e) { host.textContent = e.message; }
}
document.getElementById('status-refresh').addEventListener('click', loadStatus);

// ---- audit ----
let auditCursor = '';
async function loadAudit(reset = true) {
  try {
    const entity = encodeURIComponent(document.getElementById('audit-entity').value.trim());
    if (reset) auditCursor = '';
    const res = await api('GET', `/api/v1/admin/audit-log?entityId=${entity}&cursor=${auditCursor}`);
    auditCursor = res.nextCursor;
    const rows = res.entries.map((e) => {
      const tr = document.createElement('tr');
      const cells = [
        new Date(e.at).toLocaleString(), e.actorId, e.action,
        `${e.entity}/${e.entityId}`, e.diff ? JSON.stringify(e.diff) : '',
      ];
      cells.forEach((c) => {
        const td = document.createElement('td');
        td.textContent = c;
        tr.appendChild(td);
      });
      return tr;
    });
    const tbody = document.querySelector('#audit-table tbody');
    if (reset) tbody.replaceChildren(...rows);
    else tbody.append(...rows);
  } catch (e) { showErr(e); }
}
document.getElementById('audit-apply').addEventListener('click', () => loadAudit(true));
document.getElementById('audit-more').addEventListener('click', () => loadAudit(false));

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

const loaders = {
  customers: loadCustomers,
  users: loadUsers,
  settings: loadSettings,
  status: loadStatus,
  audit: () => loadAudit(true),
};
loadCustomers();
