<!--
  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 %}
<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 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 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 %}
<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",
    "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_ASSET_KEY    = 'sus_asset';
  const SUS_TRA_KEY      = 'sus_tra';
  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) {
    // We don't necessarily want to hide the whole contractMessage here
    // because some other stream might still be starting,
    // but usually they are sequential in loadAll.
    // However, hideContract() is called at the end of streams.
  }

  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.getElementById(`card-content-${idx}`);
  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 'aa_cb:stream_contracts_sse' %}?option=${encodeURIComponent(option)}`
    );
    const thead = document.getElementById(`contracts-header-${idx}`);
    const tbody = document.getElementById(`contracts-body-${idx}`);
    let hostileCount = 0;

    source.addEventListener('header', e => {
      // Set up column headers
      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) {
        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 'aa_cb: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) {
        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 'aa_cb: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"));
    };
  });
}


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"><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_ASSET_KEY) {
        renderCard(i, title, null, true);
        const allClean = await loadHostileAssets(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 {
        // everything 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();
}


  let forceLoadRequested = false;
  async function waitForPrewarm(option) {
    forceLoadRequested = false;
    const selectedText = dropdown.options[dropdown.selectedIndex].text;

    // 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_cb:warm_progress' %}");
          if (!res.ok) { resolve(); return; }
          const data = await res.json();
          const isStillWarming = data.users.some(u => u.user === selectedText || u.user === option);
          if (!isStillWarming) {
            resolve();
          } else {
            setTimeout(check, 1000);
          }
        } catch (e) {
          resolve();
        }
      };
      check();
    });
  }

  dropdown.addEventListener('change', async () => {
    const option = dropdown.value;
    if (!option) return;
    container.innerHTML     = '';
    errorMessages.innerHTML = '';
    hideContract();
    showGeneral('Starting cache warmer…', 'info');
    await fetch("{% url 'aa_cb:warm_cache' %}?option=" + encodeURIComponent(option));
    await waitForPrewarm(option);
    loadAll(option);
  });

// 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();
    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 and transactions if the cache isn't warm for the selected corp.</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 %}
