{% extends 'aa_bb/base.html' %}
{% load i18n %}
{% load humanize %}

{% block details %}
<!-- TomSelect CSS for searchable dropdown -->
<link rel="stylesheet"
      href="https://cdn.jsdelivr.net/npm/tom-select@2.3.1/dist/css/tom-select.css" />

<style>
    /* Base table */
    .table.compliance {
        border-collapse: collapse;
    }

    /* Header row */
    .table.compliance > thead > tr > th {
        width: 50%;
        border-top: 1px solid var(--bs-gray-dark);
        border-bottom: 1px solid var(--bs-gray-dark);
        border-left: 1px solid var(--bs-gray-dark);
        border-right: 1px solid var(--bs-gray-dark);
        background-color: var(--bs-gray-900);
    }

    /* Body rows */
    .table.compliance > tbody > tr > td {
        border-top: 1px solid var(--bs-gray-600);
        border-bottom: 1px solid var(--bs-gray-600);
        border-left: 1px solid var(--bs-gray-dark);
        border-right: 1px solid var(--bs-gray-dark);
    }

    .table.stats > thead > tr > th {
        border-top: 1px solid var(--bs-gray-dark);
        border-bottom: 1px solid var(--bs-gray-dark);
        border-left: 1px solid var(--bs-gray-dark);
        border-right: 1px solid var(--bs-gray-dark);
        background-color: var(--bs-gray-900);
    }

    /* Body rows */
    .table.stats > tbody > tr > td {
        border-top: 1px solid var(--bs-gray-600);
        border-bottom: 1px solid var(--bs-gray-600);
        border-left: 1px solid var(--bs-gray-dark);
        border-right: 1px solid var(--bs-gray-dark);
    }

    /* Progress bar styling */
    .progress-container {
        margin-bottom: 1rem;
    }
    .progress {
        height: 1.5rem;
        background-color: var(--bs-gray-800);
        border-radius: 0.25rem;
        overflow: hidden;
        border: 1px solid var(--bs-gray-700);
    }
    .progress-bar {
        height: 100%;
        color: #fff;
        text-align: center;
        line-height: 1.5rem;
        background-color: #0d6efd;
        transition: width 0.6s ease;
    }
</style>

<!-- Dropdown -->
<div class="mb-4">
  <label for="pageDropdown">{% translate "Select Account" %}</label>
  <select id="pageDropdown" class="form-control">
    <option value=""></option>
    {% for option in dropdown_options %}
      <option value="{{ option }}">{{ option }}</option>
    {% endfor %}
  </select>
</div>

<!-- Loading Spinner -->
<div id="loadingSpinner" class="text-center mb-4" style="display: none;">
  <div class="spinner-border text-primary" role="status">
    <span class="sr-only">Loading...</span>
  </div>
</div>

<!-- Error / Info Message -->
<div id="warmerBox" class="alert alert-info" style="display:none; margin-top: 1rem;">
  <div id="warmer-text"></div>
  <div class="progress mt-2" style="height: 1rem;">
    <div id="warmer-progress-bar" class="progress-bar" role="progressbar" style="width: 0%;" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100">0%</div>
  </div>
</div>
<div id="messageBox" class="alert" style="display: none;"></div>
<div id="contractMessage" class="alert alert-info" style="display:none; margin-top: 1rem;">
  <div id="contract-text"></div>
  <div class="progress mt-2" style="height: 1rem;">
    <div id="contract-progress-bar" class="progress-bar" role="progressbar" style="width: 0%;" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100">0%</div>
  </div>
</div>
<div id="errorMessages"></div>

<!-- Cards Container -->
<div id="cardsContainer"></div>

{% endblock %}

{% block extra_javascript %}
<!-- TomSelect JS (no jQuery required) -->
<script src="https://cdn.jsdelivr.net/npm/tom-select@2.3.1/dist/js/tom-select.complete.min.js"></script>

<script>
document.addEventListener('DOMContentLoaded', () => {
  const dropdown         = document.getElementById('pageDropdown');
  const container        = document.getElementById('cardsContainer');
  const spinner          = document.getElementById('loadingSpinner');
  const generalMessage   = document.getElementById('messageBox');
  const contractMessage  = document.getElementById('contractMessage');
  const errorMessages    = document.getElementById('errorMessages');
  const warmerBox        = document.getElementById('warmerBox');

  let forceLoadRequested = false;
  async function waitForPrewarm(option) {
    forceLoadRequested = false;

    // Add Force Load button to the general message
    const btn = document.createElement('button');
    btn.className = 'btn btn-xs btn-warning float-end p-1';
    btn.innerText = 'Force Load';
    btn.onclick = () => {
        forceLoadRequested = true;
        btn.disabled = true;
        btn.innerText = 'Loading...';
    };
    generalMessage.appendChild(btn);

    return new Promise((resolve) => {
      const check = async () => {
        if (forceLoadRequested) {
            resolve();
            return;
        }
        try {
          const res = await fetch("{% url 'aa_bb:warm_progress' %}");
          if (!res.ok) { resolve(); return; }
          const data = await res.json();
          const isStillWarming = data.users.some(u => u.user === option);
          if (!isStillWarming) {
            resolve();
          } else {
            setTimeout(check, 1000);
          }
        } catch (e) {
          resolve();
        }
      };
      check();
    });
  }

  // ---- Searchable, SINGLE-select account picker ----
  let accountSelect = null;
  if (window.TomSelect) {
    accountSelect = new TomSelect('#pageDropdown', {
      placeholder: '-- Choose an account --',
      allowEmptyOption: true,
      maxItems: 1,             // enforce single selection
      mode: 'single',          // single-select mode
      searchField: ['text'],
      create: false,
      closeAfterSelect: true,  // close dropdown after picking
      sortField: { field: 'text', direction: 'asc' },
      maxOptions: 5000
    });

    // Only fire once a definite account has been selected
    accountSelect.on('item_add', async function(value, item) {
      if (!value) return;
      container.innerHTML     = '';
      errorMessages.innerHTML = '';
      hideContract();
      showGeneral('Starting cache warmer…', 'info');
      await fetch("{% url 'BigBrother:warm_cache' %}?option=" + encodeURIComponent(value));
      await waitForPrewarm(value);
      loadAll(value);
    });
  } else {
    // Fallback if TomSelect fails for some reason
    dropdown.addEventListener('change', async () => {
      const value = dropdown.value;
      if (!value) return;
      container.innerHTML     = '';
      errorMessages.innerHTML = '';
      hideContract();
      showGeneral('Starting cache warmer…', 'info');
      await fetch("{% url 'BigBrother:warm_cache' %}?option=" + encodeURIComponent(value));
      await waitForPrewarm(value);
      loadAll(value);
    });
  }

  const CARD_DEFINITIONS = [
    {% for card in CARD_DEFINITIONS %}
      { title: `{{ card.title|escapejs }}`, key: `{{ card.key }}` }{% if not forloop.last %},{% endif %}
    {% endfor %}
  ];
  const MAIL_KEYWORDS = {{ MAIL_KEYWORDS|default:"[]" }};
  const MAIL_VISIBLE = [
    "sent_date", "subject",
    "sender_name", "sender_corporation", "sender_alliance",
    "recipient_names", "recipient_corps", "recipient_alliances",
    "content", "status"
  ];
  const CONTR_VISIBLE = [
    "issued_date", "end_date",
    "contract_type", "issuer_name", "issuer_corporation",
    "issuer_alliance", "assignee_name", "assignee_corporation",
    "assignee_alliance", "status", "start_location", "end_location"
  ];
  const TOTAL_CARDS      = CARD_DEFINITIONS.length;
  const SUS_CONTR_KEY    = 'sus_contr';
  const SUS_MAIL_KEY     = 'sus_mail';
  const SUS_TRA_KEY      = 'sus_tra';
  const SUS_ASSET_KEY    = 'sus_asset';
  const SUS_CLONE_KEY    = 'sus_clones';
  const SUS_CONTA_KEY    = 'sus_conta';
  let loadedNonSus       = 0;

  function showSpinner(on) {
    spinner.style.display = on ? 'block' : 'none';
  }
  function showGeneral(msg, type='info') {
    generalMessage.innerHTML    = msg;
    generalMessage.className    = 'alert alert-' + type;
    generalMessage.style.display = 'block';
  }
  function hideGeneral() {
    generalMessage.style.display = 'none';
  }
  function showContract(msg, type='info') {
    const textEl = document.getElementById('contract-text');
    if (textEl) textEl.innerHTML = msg;
    contractMessage.className    = 'alert alert-' + type;
    contractMessage.style.display = 'block';

    const bar = document.getElementById('contract-progress-bar');
    if (bar) {
        bar.style.transition = 'none';
        bar.style.width = '0%';
        bar.setAttribute('aria-valuenow', '0');
        bar.innerText = '0%';
    }
  }
  function hideContract() {
    contractMessage.style.display = 'none';
  }

  function getEmptyMessage(text) {
    return `
      <table class="table stats">
        <tbody>
          <tr>
            <td class="text-center">${text}</td>
          </tr>
        </tbody>
      </table>`;
  }

  function renderCard(idx, title, content, status) {
    const collapseId = `collapse${idx}`;
    const cardId     = `card${idx}`;
    const icon = status
      ? '<span class="text-success"><i class="fa-solid fa-circle"></i></span>'
      : '<span class="text-danger"><i class="fa-solid fa-circle"></i></span>';
    const cardDiv = document.createElement('div');
    cardDiv.id        = cardId;
    cardDiv.className = status ? 'card mb-2' : 'card mb-2 border-top-0 border-bottom-0 border-danger';
    cardDiv.innerHTML = `
      <div class="card-header d-flex justify-content-between align-items-center">
        <div class="d-flex align-items-center">
          <button class="btn btn-sm btn-link toggle-card mr-2" type="button" data-target="#${collapseId}">
            <span><i class="fa-solid fa-angle-down"></i></span>
          </button>
          <span>${title}</span>
        </div>
        ${icon}
      </div>
      <div id="${collapseId}" class="collapse">
        <div class="card-body">
          <div id="card-content-${idx}">
            ${content !== null && content !== undefined ? content : (status ? getEmptyMessage('No issues found') : getEmptyMessage('Error loading data'))}
          </div>
        </div>
      </div>
    `;
    container.appendChild(cardDiv);
    cardDiv.querySelector('.toggle-card').addEventListener('click', e => {
      document.querySelector(e.currentTarget.dataset.target).classList.toggle('collapse');
    });
  }

  function updateProgressBar(idx, current, total, text) {
    const bar = document.getElementById('contract-progress-bar');
    const textEl = document.getElementById('contract-text');
    if (!bar) return;

    const percent = total > 0 ? Math.round((current / total) * 100) : 0;
    const oldPercent = parseInt(bar.getAttribute('aria-valuenow') || '0');

    if (percent < oldPercent || percent === 0) {
        bar.style.transition = 'none';
    } else {
        bar.style.transition = 'width 0.6s ease';
    }
    bar.style.width = `${percent}%`;
    bar.setAttribute('aria-valuenow', percent);
    bar.innerText = `${percent}%`;
    if (text && textEl) textEl.innerText = text;
    contractMessage.style.display = 'block';
  }

  function hideProgressBar(idx) {
    // Top-level bar is hidden via hideContract() when appropriate
  }

  async function fetchCard(option, idx) {
    const url = `{% url 'BigBrother:load_card' %}?option=${encodeURIComponent(option)}&index=${idx}`;
    const res = await fetch(url);
    const data = await res.json();
    if (!res.ok) throw new Error(data.error || `Card ${idx+1} failed`);
    return data;
  }

  async function loadSuspiciousContracts(option, idx) {
    showContract('Loading suspicious contracts…', 'info');

    const cardBody = document.querySelector(`#collapse${idx} .card-body`);
    cardBody.innerHTML = `
      <table class="table table-striped">
        <thead>
          <tr id="contracts-header-${idx}">
            ${CONTR_VISIBLE.map(h => `<th>${h.replace(/_/g,' ')}</th>`).join('')}
          </tr>
        </thead>
        <tbody id="contracts-body-${idx}"></tbody>
      </table>`;

    return new Promise((resolve, reject) => {
      const source = new EventSource(
        `{% url 'BigBrother:stream_contracts_sse' %}?option=${encodeURIComponent(option)}`
      );
      const thead = cardBody.querySelector(`#contracts-header-${idx}`);
      const tbody = cardBody.querySelector(`#contracts-body-${idx}`);
      let hostileCount = 0;

      source.addEventListener('header', e => {
        try {
          if (e.data && e.data !== "undefined") {
            thead.innerHTML = JSON.parse(e.data);
          }
        } catch (err) {
          console.error("Failed to parse header data:", err, e.data);
        }
      });

      source.addEventListener('contract', e => {
        // Add each hostile contract row
        try {
          if (e.data && e.data !== "undefined") {
            tbody.insertAdjacentHTML('beforeend', JSON.parse(e.data));
            hostileCount++;
            // Flip icon red on first hostile
            if (hostileCount === 1) {
              updateCardStatus(idx, false);
            }
          }
        } catch (err) {
          console.error("Failed to parse contract data:", err, e.data);
        }
      });

      source.addEventListener('progress', e => {
        const [processed, total] = e.data.split(',').map(Number);
        updateProgressBar(idx, processed, total, `Checked ${processed}/${total} contracts, hostile: ${hostileCount}`);
      });

      source.addEventListener('done', () => {
        source.close();
        hideContract();
        hideProgressBar(idx);
        // If none hostile, ensure green
        const clean = hostileCount === 0;
        if (clean) {
          updateCardStatus(idx, true);
          cardBody.innerHTML = getEmptyMessage('No hostile contracts found');
        }
        resolve(clean);
      });

      source.onerror = e => {
        source.close();
        if (e.data && e.data !== "undefined") {
          try {
            const errorData = JSON.parse(e.data);
            cardBody.innerHTML = `<div class="alert alert-danger"><strong>Error:</strong> ${errorData}</div>`;
            reject(new Error(errorData));
          } catch (ex) {
            cardBody.innerHTML = `<div class="alert alert-danger"><strong>Error:</strong> ${e.data}</div>`;
            reject(new Error(e.data));
          }
        } else {
          cardBody.innerHTML = `<div class="alert alert-warning"><strong>Stream Error:</strong> Connection lost or timed out. Please wait for cache warming and try again.</div>`;
          reject(new Error("Connection error"));
        }
        updateCardStatus(idx, false);
      };
    });
  }

  // load & render hostile mails via streaming endpoint
  async function loadSuspiciousMails(option, idx) {
    showContract('Loading mails…', 'info');

    const cardBody = document.querySelector(`#collapse${idx} .card-body`);
    cardBody.innerHTML = `
      <table class="table table-striped">
        <thead>
          <tr id="mails-header-${idx}">
            ${MAIL_VISIBLE.map(h=>`<th>${h.replace(/_/g,' ')}</th>`).join('')}
          </tr>
        </thead>
        <tbody id="mails-body-${idx}"></tbody>
      </table>`;

    return new Promise((resolve, reject) => {
      const source = new EventSource(
        `{% url 'BigBrother:stream_mails_sse' %}?option=${encodeURIComponent(option)}`
      );
      const thead = cardBody.querySelector(`#mails-header-${idx}`);
      const tbody = cardBody.querySelector(`#mails-body-${idx}`);
      let hostileCount = 0;

      source.addEventListener('header', e => {
        try {
          if (e.data && e.data !== "undefined") {
            thead.innerHTML = JSON.parse(e.data);
          }
        } catch (err) {
          console.error("Failed to parse header data:", err, e.data);
        }
      });

      source.addEventListener('mail', e => {
        // Insert the hostile row
        try {
          if (e.data && e.data !== "undefined") {
            tbody.insertAdjacentHTML('beforeend', JSON.parse(e.data));
            hostileCount++;
            // On first hostile mail, immediately flip the icon red
            if (hostileCount === 1) {
              updateCardStatus(idx, false);
            }
          }
        } catch (err) {
          console.error("Failed to parse mail data:", err, e.data);
        }
      });

      source.addEventListener('progress', e => {
        const [processed, total] = e.data.split(',').map(Number);
        updateProgressBar(idx, processed, total, `Checked ${processed}/${total} mails, hostile: ${hostileCount}`);
      });

      source.addEventListener('done', () => {
        source.close();
        hideContract();
        hideProgressBar(idx);
        // If no hostile entries were seen, ensure the icon is green
        const clean = hostileCount === 0;
        if (clean) {
          updateCardStatus(idx, true);
          cardBody.innerHTML = getEmptyMessage('No hostile mails found');
        }
        resolve(clean);
      });

      source.onerror = e => {
        source.close();
        if (e.data && e.data !== "undefined") {
          try {
            const errorData = JSON.parse(e.data);
            cardBody.innerHTML = `<div class="alert alert-danger"><strong>Error:</strong> ${errorData}</div>`;
            reject(new Error(errorData));
          } catch (ex) {
            cardBody.innerHTML = `<div class="alert alert-danger"><strong>Error:</strong> ${e.data}</div>`;
            reject(new Error(e.data));
          }
        } else {
          cardBody.innerHTML = `<div class="alert alert-warning"><strong>Stream Error:</strong> Connection lost or timed out. Please wait for cache warming and try again.</div>`;
          reject(new Error("Connection error"));
        }
        updateCardStatus(idx, false);
      };
    });
  }

  async function loadSuspiciousTransactions(option, idx) {
    showContract('Loading transactions…', 'info');

    // Prepare empty table in the card-body
    const cardBody = document.getElementById(`card-content-${idx}`);
    cardBody.innerHTML = `
      <table class="table table-striped">
        <thead><tr id="tx-header-${idx}"></tr></thead>
        <tbody id="tx-body-${idx}"></tbody>
      </table>
    `;

    return new Promise((resolve, reject) => {
      const source = new EventSource(
        `{% url 'BigBrother:stream_transactions_sse' %}?option=${encodeURIComponent(option)}`
      );
      const thead = document.getElementById(`tx-header-${idx}`);
      const tbody = document.getElementById(`tx-body-${idx}`);
      let hostileCount = 0;

      source.addEventListener('header', e => {
        // render the <th>… row
        try {
          if (e.data && e.data !== "undefined") {
            thead.innerHTML = JSON.parse(e.data);
          }
        } catch (err) {
          console.error("Failed to parse transaction header data:", err, e.data);
        }
      });

      source.addEventListener('transaction', e => {
        // append each hostile <tr>
        try {
          if (e.data && e.data !== "undefined") {
            tbody.insertAdjacentHTML('beforeend', JSON.parse(e.data));
            hostileCount++;
            if (hostileCount === 1) {
              updateCardStatus(idx, false);
            }
          }
        } catch (err) {
          console.error("Failed to parse transaction data:", err, e.data);
        }
      });

      source.addEventListener('progress', e => {
        const [done, total] = e.data.split(',').map(Number);
        updateProgressBar(idx, done, total, `Checked ${done}/${total} transactions, hostile: ${hostileCount}`);
      });

      source.addEventListener('done', () => {
        source.close();
        hideContract();
        hideProgressBar(idx);
        const clean = hostileCount === 0;
        if (clean) {
          updateCardStatus(idx, true);
          cardBody.innerHTML = getEmptyMessage('No hostile transactions found');
        }
        resolve(clean);
      });

      source.onerror = e => {
        source.close();
        if (e.data && e.data !== "undefined") {
          try {
            const errorData = JSON.parse(e.data);
            cardBody.innerHTML = `<div class="alert alert-danger"><strong>Error:</strong> ${errorData}</div>`;
            reject(new Error(errorData));
          } catch (ex) {
            cardBody.innerHTML = `<div class="alert alert-danger"><strong>Error:</strong> ${e.data}</div>`;
            reject(new Error(e.data));
          }
        } else {
          cardBody.innerHTML = `<div class="alert alert-warning"><strong>Stream Error:</strong> Connection lost or timed out. Please wait for cache warming and try again.</div>`;
          reject(new Error("Connection error"));
        }
        updateCardStatus(idx, false);
      };
    });
  }

  async function loadHostileAssets(option, idx) {
    const cardBody = document.getElementById(`card-content-${idx}`);
    cardBody.innerHTML = `
      <table class="table table-striped stats">
        <thead><tr id="assets-header-${idx}"></tr></thead>
        <tbody id="assets-body-${idx}"></tbody>
      </table>`;

    return new Promise((resolve, reject) => {
      const source = new EventSource(`{% url 'BigBrother:stream_assets_sse' %}?option=${encodeURIComponent(option)}`);
      const thead = document.getElementById(`assets-header-${idx}`);
      const tbody = document.getElementById(`assets-body-${idx}`);
      let hostileCount = 0;

      source.addEventListener('header', e => {
        try {
          if (e.data && e.data !== "undefined") {
            thead.innerHTML = JSON.parse(e.data);
          }
        } catch (err) {
          console.error("Failed to parse asset header data:", err, e.data);
        }
      });
      source.addEventListener('asset', e => {
        try {
          if (e.data && e.data !== "undefined") {
            tbody.insertAdjacentHTML('beforeend', JSON.parse(e.data));
            hostileCount++;
            if (hostileCount === 1) updateCardStatus(idx, false);
          }
        } catch (err) {
          console.error("Failed to parse asset data:", err, e.data);
        }
      });
      source.addEventListener('progress', e => {
        const [processed, total] = e.data.split(',').map(Number);
        updateProgressBar(idx, processed, total, `Checked ${processed}/${total} systems, hostile: ${hostileCount}`);
      });
      source.addEventListener('done', () => {
        source.close();
        hideProgressBar(idx);
        if (hostileCount === 0) {
          updateCardStatus(idx, true);
          cardBody.innerHTML = getEmptyMessage('No assets in hostile space found');
        }
        resolve(hostileCount === 0);
      });
      source.onerror = e => {
        source.close();
        updateCardStatus(idx, false);
        reject(new Error("Stream failed"));
      };
    });
  }

  async function loadHostileClones(option, idx) {
    const cardBody = document.getElementById(`card-content-${idx}`);
    cardBody.innerHTML = `
      <table class="table table-striped stats">
        <thead><tr id="clones-header-${idx}"></tr></thead>
        <tbody id="clones-body-${idx}"></tbody>
      </table>`;

    return new Promise((resolve, reject) => {
      const source = new EventSource(`{% url 'BigBrother:stream_clones_sse' %}?option=${encodeURIComponent(option)}`);
      const thead = document.getElementById(`clones-header-${idx}`);
      const tbody = document.getElementById(`clones-body-${idx}`);
      let hostileCount = 0;

      source.addEventListener('header', e => {
        try {
          if (e.data && e.data !== "undefined") {
            thead.innerHTML = JSON.parse(e.data);
          }
        } catch (err) {
          console.error("Failed to parse clone header data:", err, e.data);
        }
      });
      source.addEventListener('clone', e => {
        try {
          if (e.data && e.data !== "undefined") {
            tbody.insertAdjacentHTML('beforeend', JSON.parse(e.data));
            hostileCount++;
            if (hostileCount === 1) updateCardStatus(idx, false);
          }
        } catch (err) {
          console.error("Failed to parse clone data:", err, e.data);
        }
      });
      source.addEventListener('progress', e => {
        const [processed, total] = e.data.split(',').map(Number);
        updateProgressBar(idx, processed, total, `Checked ${processed}/${total} systems, hostile: ${hostileCount}`);
      });
      source.addEventListener('done', () => {
        source.close();
        hideProgressBar(idx);
        if (hostileCount === 0) {
          updateCardStatus(idx, true);
          cardBody.innerHTML = getEmptyMessage('No clones in hostile space found');
        }
        resolve(hostileCount === 0);
      });
      source.onerror = e => {
        source.close();
        updateCardStatus(idx, false);
        reject(new Error("Stream failed"));
      };
    });
  }

  async function loadSuspiciousContacts(option, idx) {
    const cardBody = document.getElementById(`card-content-${idx}`);
    cardBody.innerHTML = `
      <table class="table table-striped stats">
        <thead><tr id="contacts-header-${idx}"></tr></thead>
        <tbody id="contacts-body-${idx}"></tbody>
      </table>`;

    return new Promise((resolve, reject) => {
      const source = new EventSource(`{% url 'BigBrother:stream_contacts_sse' %}?option=${encodeURIComponent(option)}`);
      const thead = document.getElementById(`contacts-header-${idx}`);
      const tbody = document.getElementById(`contacts-body-${idx}`);
      let hostileCount = 0;

      source.addEventListener('header', e => {
        try {
          if (e.data && e.data !== "undefined") {
            thead.innerHTML = JSON.parse(e.data);
          }
        } catch (err) {
          console.error("Failed to parse contact header data:", err, e.data);
        }
      });
      source.addEventListener('contact', e => {
        try {
          if (e.data && e.data !== "undefined") {
            tbody.insertAdjacentHTML('beforeend', JSON.parse(e.data));
            hostileCount++;
            if (hostileCount === 1) updateCardStatus(idx, false);
          }
        } catch (err) {
          console.error("Failed to parse contact data:", err, e.data);
        }
      });
      source.addEventListener('progress', e => {
        const [processed, total] = e.data.split(',').map(Number);
        updateProgressBar(idx, processed, total, `Checked ${processed}/${total} contacts, hostile: ${hostileCount}`);
      });
      source.addEventListener('done', () => {
        source.close();
        hideProgressBar(idx);
        if (hostileCount === 0) {
          updateCardStatus(idx, true);
          cardBody.innerHTML = getEmptyMessage('No suspicious contacts found');
        }
        resolve(hostileCount === 0);
      });
      source.onerror = e => {
        source.close();
        updateCardStatus(idx, false);
        reject(new Error("Stream failed"));
      };
    });
  }

  function updateCardStatus(idx, isClean) {
    const cardEl    = document.getElementById(`card${idx}`);
    if (!cardEl) return;
    if (isClean) {
      cardEl.classList.remove('border-top-0', 'border-bottom-0', 'border-danger');
    } else {
      cardEl.classList.add('border-top-0', 'border-bottom-0', 'border-danger');
    }
    const header    = cardEl.querySelector('.card-header');
    header.querySelectorAll('span.text-success, span.text-danger')
          .forEach(el => el.remove());
    header.insertAdjacentHTML('beforeend',
      isClean
        ? '<span class="text-success"><i class="fa-solid fa-circle"></i></span>'
        : '<span class="text-danger"><i class="fa-solid fa-circle"></i></span>'
    );
  }

  async function loadAll(option) {
    container.innerHTML     = '';
    errorMessages.innerHTML = '';
    loadedNonSus            = 0;
    hideGeneral();
    hideContract();
    showSpinner(true);

    for (let i = 0; i < CARD_DEFINITIONS.length; i++) {
      const { key, title } = CARD_DEFINITIONS[i];

      try {
        if (key === SUS_CONTR_KEY) {
          renderCard(i, title, null, true);
          const allClean = await loadSuspiciousContracts(option, i);
          updateCardStatus(i, allClean);
        }
        else if (key === SUS_MAIL_KEY) {
          renderCard(i, title, null, true);
          const allClean = await loadSuspiciousMails(option, i);
          updateCardStatus(i, allClean);
        }
        else if (key === SUS_TRA_KEY) {
          renderCard(i, title, null, true);
          const allClean = await loadSuspiciousTransactions(option, i);
          updateCardStatus(i, allClean);
        }
        else if (key === SUS_ASSET_KEY) {
          renderCard(i, title, null, true);
          const allClean = await loadHostileAssets(option, i);
          updateCardStatus(i, allClean);
        }
        else if (key === SUS_CLONE_KEY) {
          renderCard(i, title, null, true);
          const allClean = await loadHostileClones(option, i);
          updateCardStatus(i, allClean);
        }
        else if (key === SUS_CONTA_KEY) {
          renderCard(i, title, null, true);
          const allClean = await loadSuspiciousContacts(option, i);
          updateCardStatus(i, allClean);
        }
        else {
          const { content, status } = await fetchCard(option, i);
          renderCard(i, title, content, status);
        }
        loadedNonSus++;
        showGeneral(`${loadedNonSus}/${TOTAL_CARDS} loaded…`, 'info');
      } catch (err) {
        renderCard(i, title, `<p>Error: ${err.message}</p>`, false);
        console.error(err);
      }
    }

    showSpinner(false);
    hideGeneral();
  }

  // Warm-progress polling
  let progressInFlight = false;
  async function fetchProgress() {
    if (progressInFlight) return;
    progressInFlight = true;
    try {
      const res = await fetch("{% url 'aa_bb:warm_progress' %}", { keepalive: true });
      if (!res.ok) throw new Error('Progress fetch failed');
      const data = await res.json();
      const warmerText = document.getElementById('warmer-text');
      const warmerBar = document.getElementById('warmer-progress-bar');

      if (data.in_progress) {
        let currentTotal = 0;
        let currentProgress = 0;
        data.users.forEach(u => {
          currentTotal += u.total;
          currentProgress += u.current;
        });

        const parts = data.users.map(u => `${u.user} (${u.current}/${u.total})`);
        let msg = `<strong>Warming up cache for:</strong> ${parts.join(', ')}`;
        if (data.queued.count > 0) {
          msg += `<br><strong>${data.queued.count} still queued:</strong> ${data.queued.names.join(', ')}`;
        }
        msg += `<br><small>Your gunicorn is likely to kill the streams for contracts, mails and transactions if the cache isn't warm for the selected user.</small>`;

        warmerText.innerHTML = msg;
        const percent = currentTotal > 0 ? Math.round((currentProgress / currentTotal) * 100) : 0;
        const oldPercent = parseInt(warmerBar.getAttribute('aria-valuenow') || '0');

        if (percent < oldPercent || percent === 0) {
            warmerBar.style.transition = 'none';
        } else {
            warmerBar.style.transition = 'width 0.6s ease';
        }
        warmerBar.style.width = `${percent}%`;
        warmerBar.setAttribute('aria-valuenow', percent);
        warmerBar.innerText = `${percent}%`;

        if (currentTotal > 0) {
          warmerBox.style.display = 'block';
        } else {
          warmerBox.style.display = 'none';
        }
      } else {
        warmerBox.style.display = 'none';
      }
    } catch (e) {
      console.error('Warm-progress error:', e);
      warmerBox.style.display = 'none';
    } finally {
      progressInFlight = false;
    }
  }

  // Kick off immediately and then every 5s
  fetchProgress();
  setInterval(fetchProgress, 5000);
});
</script>
{% endblock %}
