<!--
  Corp Brother dashboard landing page.
  Renders the corp selector, placeholders for async card content, and the JS
  needed to stream/load each intelligence card without reloading the page.
-->
{% extends 'aa_cb/base.html' %}
{% load i18n %}
{% load humanize %}

{% block details %}

<!-- Dropdown -->
<div class="mb-4">
  <label for="pageDropdown">{% translate "Select Corp" %}</label>
  <select id="pageDropdown" class="form-control">
    <option value="" disabled selected>-- Choose a corp --</option>
    {% for corp_id, corp_name in dropdown_options %}
      <option value="{{ corp_id }}">{{ corp_name }}</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>
<div id="messageBox" class="alert" style="display: none;"></div>
<div id="contractMessage" class="alert" style="display:none;"></div>
<div id="errorMessages"></div>

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

{% endblock %}

{% block extra_javascript %}
<script>
document.addEventListener('DOMContentLoaded', () => {
  const CARD_DEFINITIONS = [
    {% for card in CARD_DEFINITIONS %}
      { title: `{{ card.title|escapejs }}`, key: `{{ card.key }}` }{% if not forloop.last %},{% endif %}
    {% endfor %}
  ];
  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');  // second box
  const errorMessages    = document.getElementById('errorMessages');
  const MAIL_KEYWORDS = {{ MAIL_KEYWORDS|default:"[]" }};
  const MAIL_VISIBLE = [
    "sent_date", "subject",
    "sender_name", "sender_corporation", "sender_alliance",
    "recipient_names", "recipient_corps", "recipient_alliances", "status"
  ];
  const CONTR_VISIBLE = [
    "issued_date", "end_date",
    "contract_type", "issuer_name", "issuer_corporation",
    "issuer_alliance", "assignee_name", "assignee_corporation",
    "assignee_alliance", "status"
  ];
  const TOTAL_CARDS      = CARD_DEFINITIONS.length;
  const SUS_CONTR_KEY    = 'sus_contr';
  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') {
    contractMessage.innerHTML    = msg;
    contractMessage.className    = 'alert alert-' + type;
    contractMessage.style.display = 'block';
  }
  function hideContract() {
    contractMessage.style.display = 'none';
  }

function renderCard(idx, title, content, status) {
  const collapseId = `collapse${idx}`;
  const cardId     = `card${idx}`;           // NEW
  const icon = status
    ? '<span class="text-success">&#128994;</span>'
    : '<span class="text-danger">&#128681;</span>';
  const cardDiv = document.createElement('div');
  cardDiv.id        = cardId;                // NEW
  cardDiv.className = 'card mb-2';
  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>&#9660;</span>
        </button>
        <span>${title}</span>
      </div>
      ${icon}
    </div>
    <div id="${collapseId}" class="collapse">
      <div class="card-body">${content}</div>
    </div>
  `;
  container.appendChild(cardDiv);
  cardDiv.querySelector('.toggle-card').addEventListener('click', e => {
    document.querySelector(e.currentTarget.dataset.target).classList.toggle('collapse');
  });
}

  async function fetchCard(option, idx) {
    const url = `{% url 'aa_cb: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');

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

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

    source.addEventListener('header', e => {
      // Set up column headers
      thead.innerHTML = JSON.parse(e.data);
    });

    source.addEventListener('contract', e => {
      // Add each hostile contract row
      tbody.insertAdjacentHTML('beforeend', JSON.parse(e.data));
      hostileCount++;
      // Flip icon red on first hostile
      if (hostileCount === 1) {
        updateCardStatus(idx, false);
      }
    });

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

    source.addEventListener('done', () => {
      source.close();
      hideContract();
      // If none hostile, ensure green
      const clean = hostileCount === 0;
      if (clean) {
        updateCardStatus(idx, true);

        // **NEW**: inject a “no hostile” row
        const tbody = cardBody.querySelector('#contracts-body');
        const colspan = thead.querySelectorAll('th').length;
        tbody.insertAdjacentHTML('beforeend',
          `<tr><td colspan="${colspan}" class="text-center text-muted">
             No hostile contracts found
           </td></tr>`
        );
      }
      resolve(clean);
    });

    source.onerror = err => {
      source.close();
      const errorMessages = document.getElementById('errorMessages');
      errorMessages.innerHTML = '<div class="alert alert-warning">Contract stream failed, please wait for the cache to warm up and try again or get your IT to increase(or set to 0) your gunicorn timeout in supervisor.conf.</div>';
      updateCardStatus(idx, true);
      reject(err);
    };
  });
}


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

  // Prepare empty table in the card-body
  const cardBody = document.querySelector(`#collapse${idx} .card-body`);
  //const headersCols = /* same list as server: you can hardcode or fetch via API */;
  cardBody.innerHTML = `
    <table class="table table-striped">
      <thead><tr id="tx-header"></tr></thead>
      <tbody id="tx-body"></tbody>
    </table>
  `;

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

    source.addEventListener('header', e => {
      // render the <th>… row
      thead.innerHTML = JSON.parse(e.data);
    });

    source.addEventListener('transaction', e => {
      // append each hostile <tr>
      tbody.insertAdjacentHTML('beforeend', JSON.parse(e.data));
      hostileCount++;
      if (hostileCount === 1) {
        updateCardStatus(idx, false);
      }
    });

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

    source.addEventListener('done', () => {
      source.close();
      hideContract();
      const clean = hostileCount === 0;
      if (clean) {
        updateCardStatus(idx, true);
        // inject “no hostile” row
        const colspan = thead.querySelectorAll('th').length || 1;
        tbody.insertAdjacentHTML('beforeend',
          `<tr><td colspan="${colspan}" class="text-center text-muted">
             No hostile transactions found
           </td></tr>`
        );
      }
      resolve(clean);
    });

    source.onerror = err => {
      source.close();
      document.getElementById('errorMessages').innerHTML =
        '<div class="alert alert-warning">Transaction stream failed, please wait for the cache to warm up and try again or get your IT to increase(or set to 0) your gunicorn timeout in supervisor.conf</div>';
      updateCardStatus(idx, true);
      reject(err);
    };
  });
}



function updateCardStatus(idx, isClean) {
  const cardEl    = document.getElementById(`card${idx}`);
  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">&#128994;</span>'
      : '<span class="text-danger">&#128681;</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) {
        // 1) Render the card with an empty contracts table:
        renderCard(i, title,
          `<table class="table table-striped">
             <thead><tr id="contracts-header"></tr></thead>
             <tbody id="contracts-body"></tbody>
           </table>`,
          true // assume clean until a hostile contract is detected
        );

        // 2) Stream the contracts; returns allClean flag
        const allClean = await loadSuspiciousContracts(option, i);

        // 3) Update dot color based on whether any hostile were found
        updateCardStatus(i, allClean);
        loadedNonSus++;
        showGeneral(
          `${loadedNonSus}/${TOTAL_CARDS} loaded… This may take a while, do not refresh`,
          'info'
        );
      }else if (key === 'sus_tra') {
        renderCard(i, title,
          `<table class="table table-striped">
            <thead><tr id="tx-header"></tr></thead>
            <tbody id="tx-body"></tbody>
          </table>`,
          true
        );
        const allClean = await loadSuspiciousTransactions(option, i);
        updateCardStatus(i, allClean);
        loadedNonSus++;
        showGeneral(
          `${loadedNonSus}/${TOTAL_CARDS} loaded…`, 'info'
        );
      }
      else {
        // everything else
        const { content, status } = await fetchCard(option, i);
        loadedNonSus++;
        showGeneral(
          `${loadedNonSus}/${TOTAL_CARDS} loaded… This may take a while, do not refresh`,
          'info'
        );
        renderCard(i, title, content, status);
      }
    } catch (err) {
      renderCard(i, title, `<p>Error: ${err.message}</p>`, false);
      console.error(err);
    }
  }

  showSpinner(false);
  hideGeneral();
}


dropdown.addEventListener('change', () => {
  fetch("{% url 'aa_cb:warm_cache' %}?option=" + encodeURIComponent(dropdown.value));
  loadAll(dropdown.value);
});

// Poll every 5 seconds for warming-progress updates
const warmerBox = document.getElementById('warmerBox');
let progressInFlight = false;
async function fetchProgress() {
  if (progressInFlight) return;
  progressInFlight = true;
  try {
    const res = await fetch("{% url 'aa_cb:warm_progress' %}", { keepalive: true });
    if (!res.ok) throw new Error('Progress fetch failed');
    const data = await res.json();
     if (data.in_progress) {
      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>Your gunicorn is likely to kill the streams for contracts and transactions if the cache isn't warm for the selected user(it starts warming up the 1st time you select it so try and if it falls, go touch grass or something until this is done or get your IT to increase(or set to 0) your gunicorn timeout in supervisor.conf)`;
      warmerBox.innerHTML = msg;
      warmerBox.style.display = 'block';
    } 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 %}
