/* ============================================================
   Sovereign Orchestrator — Frontend Application
   Pure vanilla JS — no frameworks, no libraries
   ============================================================ */

const App = (() => {
  'use strict';

  // ── State ─────────────────────────────────────────────────
  const state = {
    currentSection: 'dashboard',
    systemStatus: null,
    builds: [],
    isos: [],
    vms: {},          // vmid -> status data
    pollTimer: null,
    activeBuildId: null,
    buildPollTimer: null,
  };

  // ── DOM Refs ──────────────────────────────────────────────
  const $ = (sel, ctx = document) => ctx.querySelector(sel);
  const $$ = (sel, ctx = document) => [...ctx.querySelectorAll(sel)];

  // ── API Helper ────────────────────────────────────────────
  async function api(method, path, body = null) {
    const opts = {
      method,
      headers: { 'Content-Type': 'application/json' },
    };
    if (body) opts.body = JSON.stringify(body);
    const res = await fetch(path, opts);
    if (!res.ok) {
      let detail = `HTTP ${res.status}`;
      try {
        const j = await res.json();
        detail = j.detail || j.message || detail;
      } catch (_) { /* ignore */ }
      throw new Error(detail);
    }
    return res.json();
  }

  // ── Toast System ──────────────────────────────────────────
  const TOAST_ICONS = {
    success: '✅', error: '❌', info: 'ℹ️', warning: '⚠️',
  };

  function toast(type, message, duration = 4500) {
    const container = $('#toast-container');
    const el = document.createElement('div');
    el.className = `toast ${type}`;
    el.innerHTML = `
      <span class="toast-icon">${TOAST_ICONS[type] || ''}</span>
      <span class="toast-message">${escHtml(message)}</span>
    `;
    container.appendChild(el);
    setTimeout(() => {
      el.classList.add('leaving');
      el.addEventListener('animationend', () => el.remove());
    }, duration);
  }

  function escHtml(s) {
    const d = document.createElement('div');
    d.textContent = s;
    return d.innerHTML;
  }

  // ── Navigation ────────────────────────────────────────────
  function initNav() {
    $$('.nav-link').forEach(link => {
      link.addEventListener('click', (e) => {
        e.preventDefault();
        navigateTo(link.dataset.section);
      });
    });

    // Quick action cards
    $$('.action-card[data-goto]').forEach(card => {
      card.addEventListener('click', () => navigateTo(card.dataset.goto));
    });

    // Mobile toggle
    const toggle = $('#mobile-toggle');
    const sidebar = $('#sidebar');
    toggle.addEventListener('click', () => sidebar.classList.toggle('open'));

    // Close sidebar on mobile when nav clicked
    $$('.nav-link').forEach(link => {
      link.addEventListener('click', () => sidebar.classList.remove('open'));
    });

    // Handle hash on load
    const hash = location.hash.replace('#', '');
    if (hash && $(`[data-section="${hash}"]`)) {
      navigateTo(hash);
    }
  }

  function navigateTo(section) {
    state.currentSection = section;

    // Update nav
    $$('.nav-link').forEach(l => l.classList.remove('active'));
    const activeLink = $(`.nav-link[data-section="${section}"]`);
    if (activeLink) activeLink.classList.add('active');

    // Update sections
    $$('.section').forEach(s => s.classList.remove('active'));
    const activeSection = $(`#section-${section}`);
    if (activeSection) {
      activeSection.classList.remove('active');
      // Force reflow for animation
      void activeSection.offsetWidth;
      activeSection.classList.add('active');
    }

    location.hash = section;

    // Section-specific loads
    if (section === 'deployments') {
      loadBuilds();
      loadISOs();
    } else if (section === 'dashboard') {
      refreshAll();
    }
  }

  // ── System Status ─────────────────────────────────────────
  async function fetchStatus() {
    try {
      const data = await api('GET', '/api/status');
      state.systemStatus = data;
      updateStatusUI(data);
    } catch (err) {
      updateStatusUI(null, err.message);
    }
  }

  function updateStatusUI(data, error) {
    const badge = $('#system-status-badge');
    const dashStatus = $('#dash-system-status');
    const indicator = $('#dash-status-indicator');

    if (error) {
      badge.className = 'status-badge error';
      badge.querySelector('.status-text').textContent = 'Offline';
      dashStatus.textContent = 'Offline';
      if (indicator) indicator.innerHTML = '<span class="pulse-dot" style="background:var(--red)"></span>';
      return;
    }

    badge.className = 'status-badge';
    badge.querySelector('.status-text').textContent = 'System Online';
    dashStatus.textContent = data.status || 'Online';

    // Update dashboard counts if available
    if (data.iso_count !== undefined) $('#dash-iso-count').textContent = data.iso_count;
    if (data.build_count !== undefined) $('#dash-build-count').textContent = data.build_count;
    if (data.vm_count !== undefined) $('#dash-vm-count').textContent = data.vm_count;
  }

  // ── Builds ────────────────────────────────────────────────
  async function loadBuilds() {
    try {
      const data = await api('GET', '/api/builds');
      state.builds = Array.isArray(data) ? data : (data.builds || []);
      renderBuilds();
      renderTimeline();
      $('#dash-build-count').textContent = state.builds.length;
    } catch (err) {
      // Silently handle — builds endpoint may not exist yet
      console.warn('Failed to load builds:', err.message);
    }
  }

  function renderBuilds() {
    const container = $('#builds-list');
    if (!state.builds.length) {
      container.innerHTML = `
        <div class="empty-state">
          <span class="empty-icon">📦</span>
          <p>No builds found.</p>
        </div>`;
      return;
    }

    container.innerHTML = state.builds.map(b => {
      const statusClass = b.status === 'success' || b.status === 'completed' ? 'success'
                        : b.status === 'error' || b.status === 'failed' ? 'error'
                        : 'pending';
      return `
        <div class="build-item" data-build-id="${escHtml(b.id || b.build_id || '')}">
          <span class="build-status-dot ${statusClass}"></span>
          <div class="build-info">
            <div class="build-name">${escHtml(b.fqdn || b.name || b.id || 'Build')}</div>
            <div class="build-detail">${escHtml(b.id || b.build_id || '')}</div>
          </div>
          <span class="build-badge ${statusClass}">${escHtml(b.status || 'unknown')}</span>
        </div>`;
    }).join('');

    // Click to view details
    $$('.build-item', container).forEach(item => {
      item.addEventListener('click', () => viewBuild(item.dataset.buildId));
    });
  }

  function renderTimeline() {
    const container = $('#recent-builds-timeline');
    const recent = state.builds.slice(0, 8);

    if (!recent.length) {
      container.innerHTML = `
        <div class="empty-state">
          <span class="empty-icon">📦</span>
          <p>No builds yet. Generate your first ISO to get started.</p>
        </div>`;
      return;
    }

    container.innerHTML = recent.map(b => {
      const statusClass = b.status === 'success' || b.status === 'completed' ? 'success'
                        : b.status === 'error' || b.status === 'failed' ? 'error'
                        : 'pending';
      const timeStr = b.created_at || b.timestamp || '';
      return `
        <div class="timeline-item">
          <div class="timeline-dot ${statusClass}"></div>
          <div class="timeline-content">
            <div class="timeline-title">${escHtml(b.fqdn || b.name || 'Build')}</div>
            <div class="timeline-meta">
              <span class="timeline-id">${escHtml(b.id || b.build_id || '')}</span>
              ${timeStr ? ` · ${escHtml(formatTime(timeStr))}` : ''}
            </div>
          </div>
        </div>`;
    }).join('');
  }

  async function viewBuild(buildId) {
    if (!buildId) return;
    try {
      const data = await api('GET', `/api/builds/${buildId}`);
      const logCard = $('#build-log-card');
      const logOutput = $('#build-log-output');
      logCard.hidden = false;
      logOutput.textContent = data.log || data.output || JSON.stringify(data, null, 2);
      logOutput.scrollTop = logOutput.scrollHeight;
      navigateTo('iso-builder');
    } catch (err) {
      toast('error', `Failed to load build: ${err.message}`);
    }
  }

  // ── ISOs ──────────────────────────────────────────────────
  async function loadISOs() {
    try {
      const data = await api('GET', '/api/isos');
      state.isos = Array.isArray(data) ? data : (data.isos || []);
      renderISOSelect();
      $('#dash-iso-count').textContent = state.isos.length;
    } catch (err) {
      console.warn('Failed to load ISOs:', err.message);
    }
  }

  function renderISOSelect() {
    const select = $('#deploy-iso');
    // Keep the first placeholder option
    const placeholder = select.querySelector('option');
    select.innerHTML = '';
    select.appendChild(placeholder);

    state.isos.forEach(iso => {
      const opt = document.createElement('option');
      const name = typeof iso === 'string' ? iso : (iso.name || iso.filename || iso.id || '');
      opt.value = name;
      opt.textContent = name;
      select.appendChild(opt);
    });
  }

  // ── ISO Builder Form ─────────────────────────────────────
  function initISOForm() {
    const form = $('#iso-form');
    form.addEventListener('submit', async (e) => {
      e.preventDefault();
      const btn = $('#iso-submit-btn');
      setButtonLoading(btn, true);

      const formData = new FormData(form);
      const payload = {};
      for (const [key, value] of formData.entries()) {
        if (key === 'ssh_keys') {
          payload[key] = value.split('\n').map(k => k.trim()).filter(Boolean);
        } else if (key === 'disk_list') {
          payload[key] = value.split(',').map(d => d.trim()).filter(Boolean);
        } else {
          payload[key] = value;
        }
      }

      try {
        const data = await api('POST', '/api/generate-iso', payload);
        toast('success', 'ISO generation started!');

        // Show build log
        const logCard = $('#build-log-card');
        const logOutput = $('#build-log-output');
        logCard.hidden = false;
        logOutput.textContent = '▶ Build initiated…\n';

        // If we got a build ID, start polling
        const buildId = data.build_id || data.id;
        if (buildId) {
          state.activeBuildId = buildId;
          pollBuildLog(buildId);
        } else {
          logOutput.textContent += JSON.stringify(data, null, 2);
        }
      } catch (err) {
        toast('error', `ISO generation failed: ${err.message}`);
      } finally {
        setButtonLoading(btn, false);
      }
    });
  }

  async function pollBuildLog(buildId) {
    // Clear previous timer
    if (state.buildPollTimer) clearInterval(state.buildPollTimer);

    const logOutput = $('#build-log-output');

    const poll = async () => {
      try {
        const data = await api('GET', `/api/builds/${buildId}`);
        const log = data.log || data.output || '';
        if (log) {
          logOutput.textContent = log;
          logOutput.scrollTop = logOutput.scrollHeight;
        }

        const status = data.status || '';
        if (status === 'success' || status === 'completed') {
          toast('success', 'ISO build completed successfully!');
          clearInterval(state.buildPollTimer);
          state.buildPollTimer = null;
          loadBuilds();
        } else if (status === 'error' || status === 'failed') {
          toast('error', 'ISO build failed.');
          clearInterval(state.buildPollTimer);
          state.buildPollTimer = null;
          loadBuilds();
        }
      } catch (_) { /* ignore poll errors */ }
    };

    await poll();
    state.buildPollTimer = setInterval(poll, 3000);
  }

  // ── Deploy Form ───────────────────────────────────────────
  function initDeployForm() {
    const form = $('#deploy-form');
    form.addEventListener('submit', async (e) => {
      e.preventDefault();
      const btn = $('#deploy-submit-btn');
      setButtonLoading(btn, true);

      const formData = new FormData(form);
      const payload = {};
      for (const [key, value] of formData.entries()) {
        if (['vmid', 'cores', 'memory', 'disk_size'].includes(key)) {
          payload[key] = parseInt(value, 10);
        } else {
          payload[key] = value;
        }
      }

      try {
        const data = await api('POST', '/api/deploy', payload);
        toast('success', `VM ${payload.vmid} deployment started!`);
        // Add the VM to watched list
        if (payload.vmid) {
          addVMCard(payload.vmid);
        }
      } catch (err) {
        toast('error', `Deployment failed: ${err.message}`);
      } finally {
        setButtonLoading(btn, false);
      }
    });
  }

  // ── VM Control ────────────────────────────────────────────
  function initVMControl() {
    const lookupBtn = $('#vm-lookup-btn');
    const lookupInput = $('#vm-lookup-id');

    lookupBtn.addEventListener('click', () => {
      const vmid = parseInt(lookupInput.value, 10);
      if (vmid && vmid >= 100) {
        addVMCard(vmid);
        lookupInput.value = '';
      } else {
        toast('warning', 'Enter a valid VM ID (≥ 100)');
      }
    });

    lookupInput.addEventListener('keydown', (e) => {
      if (e.key === 'Enter') {
        e.preventDefault();
        lookupBtn.click();
      }
    });
  }

  async function addVMCard(vmid) {
    const grid = $('#vm-cards-grid');
    // Remove empty state
    const empty = grid.querySelector('.empty-state');
    if (empty) empty.remove();

    // Don't duplicate
    if ($(`#vm-card-${vmid}`, grid)) {
      fetchVMStatus(vmid);
      return;
    }

    const card = document.createElement('div');
    card.className = 'glass-card vm-card';
    card.id = `vm-card-${vmid}`;
    card.innerHTML = `
      <div class="vm-card-header">
        <div class="vm-card-title">🖥️ VM <span>${vmid}</span></div>
        <div class="vm-status-tag unknown">
          <span class="tag-dot"></span>
          <span class="tag-label">Loading…</span>
        </div>
      </div>
      <div class="vm-card-body">
        <div class="vm-info-grid">
          <div class="vm-info-item">
            <div class="vm-info-label">Status</div>
            <div class="vm-info-value" data-field="status">—</div>
          </div>
          <div class="vm-info-item">
            <div class="vm-info-label">Node</div>
            <div class="vm-info-value" data-field="node">—</div>
          </div>
          <div class="vm-info-item">
            <div class="vm-info-label">CPU</div>
            <div class="vm-info-value" data-field="cpu">—</div>
          </div>
          <div class="vm-info-item">
            <div class="vm-info-label">Memory</div>
            <div class="vm-info-value" data-field="mem">—</div>
          </div>
        </div>
        <div class="vm-card-actions">
          <button class="btn btn-success btn-sm" onclick="App.vmAction(${vmid}, 'start')">
            <span class="btn-icon">⚡</span> Start
          </button>
          <button class="btn btn-danger btn-sm" onclick="App.vmAction(${vmid}, 'stop')">
            <span class="btn-icon">⏹️</span> Stop
          </button>
          <button class="btn btn-ghost btn-sm" onclick="App.fetchVMStatus(${vmid})">
            <span class="btn-icon">🔄</span> Refresh
          </button>
        </div>
      </div>
    `;

    grid.appendChild(card);
    card.style.animation = 'fadeSlideIn 0.4s ease both';

    await fetchVMStatus(vmid);
  }

  async function fetchVMStatus(vmid) {
    const card = $(`#vm-card-${vmid}`);
    if (!card) return;

    try {
      const data = await api('GET', `/api/vm/${vmid}/status`);
      state.vms[vmid] = data;
      updateVMCard(vmid, data);
    } catch (err) {
      updateVMCard(vmid, { status: 'unknown', error: err.message });
    }
  }

  function updateVMCard(vmid, data) {
    const card = $(`#vm-card-${vmid}`);
    if (!card) return;

    const status = (data.status || 'unknown').toLowerCase();
    const isRunning = status === 'running';
    const isStopped = status === 'stopped';

    // Status tag
    const tag = card.querySelector('.vm-status-tag');
    tag.className = `vm-status-tag ${isRunning ? 'running' : isStopped ? 'stopped' : 'unknown'}`;
    tag.querySelector('.tag-label').textContent = capitalize(status);

    // Info fields
    const setField = (name, val) => {
      const el = card.querySelector(`[data-field="${name}"]`);
      if (el) el.textContent = val;
    };

    setField('status', capitalize(status));
    setField('node', data.node || '—');
    setField('cpu', data.cpus || data.cores || '—');
    setField('mem', data.maxmem ? formatBytes(data.maxmem) : (data.memory ? `${data.memory} MB` : '—'));
  }

  async function vmAction(vmid, action) {
    const actionPath = action === 'start' ? 'start' : 'stop';
    try {
      toast('info', `${capitalize(action)}ing VM ${vmid}…`);
      await api('POST', `/api/vm/${vmid}/${actionPath}`);
      toast('success', `VM ${vmid} ${action} command sent!`);
      // Refresh after a short delay
      setTimeout(() => fetchVMStatus(vmid), 2000);
    } catch (err) {
      toast('error', `Failed to ${action} VM ${vmid}: ${err.message}`);
    }
  }

  // ── Polling ───────────────────────────────────────────────
  function startPolling() {
    // Initial fetch
    fetchStatus();
    loadBuilds();
    loadISOs();

    // Poll every 5 seconds
    state.pollTimer = setInterval(() => {
      fetchStatus();

      // Refresh VM statuses
      for (const vmid of Object.keys(state.vms)) {
        fetchVMStatus(vmid);
      }
    }, 5000);
  }

  // ── Utility ───────────────────────────────────────────────
  function setButtonLoading(btn, loading) {
    const text = btn.querySelector('.btn-text');
    const spinner = btn.querySelector('.btn-spinner');
    if (loading) {
      btn.disabled = true;
      if (text) text.style.opacity = '0.6';
      if (spinner) spinner.hidden = false;
    } else {
      btn.disabled = false;
      if (text) text.style.opacity = '1';
      if (spinner) spinner.hidden = true;
    }
  }

  function capitalize(str) {
    if (!str) return '';
    return str.charAt(0).toUpperCase() + str.slice(1);
  }

  function formatBytes(bytes) {
    if (!bytes || bytes === 0) return '0 B';
    const k = 1024;
    const sizes = ['B', 'KB', 'MB', 'GB', 'TB'];
    const i = Math.floor(Math.log(bytes) / Math.log(k));
    return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i];
  }

  function formatTime(ts) {
    try {
      const d = new Date(ts);
      if (isNaN(d.getTime())) return ts;
      const now = new Date();
      const diff = now - d;
      if (diff < 60000) return 'Just now';
      if (diff < 3600000) return `${Math.floor(diff / 60000)}m ago`;
      if (diff < 86400000) return `${Math.floor(diff / 3600000)}h ago`;
      return d.toLocaleDateString('en-US', { month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' });
    } catch (_) {
      return ts;
    }
  }

  // ── Refresh All ───────────────────────────────────────────
  async function refreshAll() {
    await Promise.all([
      fetchStatus(),
      loadBuilds(),
      loadISOs(),
    ]);
    toast('info', 'Data refreshed');
  }

  // ── Init ──────────────────────────────────────────────────
  function init() {
    initNav();
    initISOForm();
    initDeployForm();
    initVMControl();
    startPolling();
  }

  // Boot
  if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', init);
  } else {
    init();
  }

  // ── Public API ────────────────────────────────────────────
  return {
    refreshAll,
    fetchVMStatus,
    vmAction,
  };
})();
