// ========== GRID & STATUS HELPERS ==========
(function () {

  /* Enhanced status helpers with response time tracking */
  function setQuick(id, up, responseTime = null) {
    const dot = document.getElementById(id + '-dot');
    const pill = document.getElementById(id + '-pill');
    const timeEl = document.getElementById(id + '-time');
    const card = document.querySelector(`[data-app="${id}"]`);

    if (dot) {
      dot.classList.toggle('ok', up);
      dot.classList.toggle('bad', !up);
    }

    if (pill) {
      pill.textContent = up ? 'ON' : 'OFF';
      pill.classList.toggle('on', up);
      pill.classList.toggle('off', !up);
    }

    if (timeEl && responseTime !== null) {
      timeEl.textContent = up ? `${responseTime}ms` : 'timeout';
      timeEl.className = `response-time ${getResponseTimeClass(responseTime, up)}`;
    }

    // Update card status for icon coloring
    if (card) {
      card.setAttribute('data-status', up ? 'on' : 'off');
    }

  }

  function getResponseTimeClass(time, isUp) {
    if (!isUp) return 'timeout';
    if (time < 200) return 'excellent';
    if (time < 500) return 'good';
    if (time < 1000) return 'fair';
    return 'slow';
  }

  async function checkServiceWithTiming(id) {
    const startTime = performance.now();
    try {
      const r = await fetch('/probe/' + id, { cache: 'no-store' });
      const endTime = performance.now();
      const responseTime = Math.round(endTime - startTime);
      const isUp = (r.status >= 200 && r.status < 400) || r.status === 401 || r.status === 403;
      return { isUp, responseTime };
    } catch {
      const endTime = performance.now();
      const responseTime = Math.round(endTime - startTime);
      return { isUp: false, responseTime };
    }
  }

  /* App grid - loaded from API */
  window.APPS = []; // Use window.APPS as the main array
  let refreshInFlight = null;
  let refreshQueued = false;

  // Load services from API
  async function loadServices() {
    try {
      if (window.SkeletonLoader) window.SkeletonLoader.show(6);
      const response = await fetch('/api/v1/services', { cache: 'no-store' });
      if (response.ok) {
        const result = await response.json();
        // Standard envelope: { success: true, services: [...], pagination?: {...} }
        window.APPS = result.services || [];
        if (window.SkeletonLoader) window.SkeletonLoader.hide();
      } else {
        console.error('Failed to load services:', response.status);
        if (window.SkeletonLoader) window.SkeletonLoader.hide();
      }
    } catch (error) {
      console.error('Failed to load services:', error);
      if (window.SkeletonLoader) window.SkeletonLoader.hide();
    }
  }

  function serviceUrl(id) {
    const svc = window.APPS?.find(a => a.id === id);
    if (svc?.url) return svc.url.startsWith('http') ? svc.url : 'https://' + svc.url;
    if (svc?.isExternal && svc.externalUrl) return svc.externalUrl;
    const dns = SITE.dnsServers?.[id];
    if (dns) return 'http://' + dns.ip + ':' + (dns.port || 5380);
    return buildServiceUrl(id);
  }
  function el(tag, cls, txt) { const n = document.createElement(tag); if (cls) n.className = cls; if (txt) n.textContent = txt; return n; }

  function buildGrid() {
    const root = document.getElementById('cards'); root.innerHTML = '';
    for (let i = 0; i < window.APPS.length; i++) {
      const s = window.APPS[i];
      if (s.id === 'ca') continue; // DashCA lives in top anchor row
      const card = el('div', 'card');
      card.setAttribute('data-app', s.id);
      card.setAttribute('data-status', 'off'); // Initial status
      if (s.containerId) card.setAttribute('data-container-id', s.containerId);
      if (s.category) card.setAttribute('data-category', s.category);
      if (s.recipeId) card.setAttribute('data-recipe-id', s.recipeId);

      const dot = el('span', 'dot bad at-bl'); dot.id = 'dot-' + s.id + '-grid'; card.appendChild(dot);

      const row = el('div', 'row');
      const wrap = el('div', 'logo-wrap');

      // Use reliable PNG images with automatic CDN fallback
      const img = document.createElement('img');
      img.src = s.logo;
      img.alt = s.name;
      img.className = 'logo-img';
      img.onerror = function() {
        // Try CDN fallback with multiple naming strategies
        // Use id, appTemplate, or derive from name
        let appId = s.id || s.appTemplate;
        if (!appId && s.name) {
          // Derive ID from name (lowercase, remove spaces)
          appId = s.name.toLowerCase().replace(/\s+/g, '-');
        }

        if (appId) {
          // Try different CDN URL formats
          const cdnUrls = [
            `https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/${appId}.png`,
            `https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/${appId.toLowerCase()}.png`,
            `https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/${appId.replace(/-/g, '')}.png`,
            `https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/${s.name.toLowerCase().replace(/\s+/g, '-')}.png`
          ];

          // Remove duplicates
          const uniqueUrls = [...new Set(cdnUrls)];

          // Find the next URL to try
          const currentIndex = uniqueUrls.indexOf(this.src);
          const nextIndex = currentIndex + 1;

          if (nextIndex < uniqueUrls.length) {
            this.src = uniqueUrls[nextIndex];
          } else {
            this.style.display = 'none';
          }
        } else {
          this.style.display = 'none';
        }
      };

      wrap.appendChild(img);
      row.appendChild(wrap);

      const nameSpan = el('span', 'name', s.name);
      row.appendChild(nameSpan);

      // Add Tailscale badge if service is protected
      if (s.tailscaleOnly) {
        const tsBadge = el('span', 'ts-badge', '🔐');
        tsBadge.title = 'Tailscale-only access';
        tsBadge.style.cssText = 'margin-left: 6px; font-size: 0.75rem; opacity: 0.8;';
        nameSpan.appendChild(tsBadge);
      }

      // Add Category badge if service has one (colored pill with icon)
      if (s.category) {
        const cats = (typeof DC !== 'undefined' && DC.CATEGORIES) || window.DC_CATEGORIES || {};
        const catInfo = cats[s.category] || {};
        const catBadge = el('span', 'cat-badge', `${catInfo.icon || ''} ${s.category}`.trim());
        catBadge.title = `Category: ${s.category}`;
        catBadge.style.cssText = `margin-left: 6px; font-size: 0.65rem; padding: 1px 6px; border-radius: 999px; background: color-mix(in srgb, ${catInfo.color || '#7f8c8d'} 25%, transparent); color: ${catInfo.color || '#7f8c8d'}; border: 1px solid color-mix(in srgb, ${catInfo.color || '#7f8c8d'} 50%, transparent); white-space: nowrap; font-weight: 500;`;
        nameSpan.appendChild(catBadge);
      }

      row.appendChild(el('span', 'spacer'));

      const pill = el('span', 'badge off', 'OFF'); pill.id = 'badge-' + s.id; row.appendChild(pill);

      // Update available badge (hidden by default, shown when update detected)
      const updateBadge = el('span', 'update-available-badge', 'UPDATE');
      updateBadge.id = 'update-badge-' + s.id;
      updateBadge.title = 'Update available';
      row.appendChild(updateBadge);

      card.appendChild(row);

      // Add response time row
      const responseRow = el('div', 'response-row');
      const timeSpan = el('span', 'response-time', '--'); timeSpan.id = 'time-' + s.id;
      responseRow.appendChild(timeSpan);
      card.appendChild(responseRow);

      // Add health/uptime row
      const healthRow = el('div', 'health-row');
      healthRow.id = 'health-' + s.id;
      const uptimeChip = el('span', 'uptime-chip', '--');
      uptimeChip.id = 'uptime-' + s.id;
      healthRow.appendChild(uptimeChip);
      const uptimeMiniBar = document.createElement('div');
      uptimeMiniBar.className = 'uptime-mini-bar';
      const uptimeFill = document.createElement('div');
      uptimeFill.className = 'fill';
      uptimeFill.id = 'uptime-bar-' + s.id;
      uptimeFill.style.width = '0%';
      uptimeMiniBar.appendChild(uptimeFill);
      healthRow.appendChild(uptimeMiniBar);
      card.appendChild(healthRow);

      const btnRow = el('div', 'btn-row');

      // Add logs button for services with containerIds
      if (s.containerId) {
        const logsBtn = el('button', 'logs-btn', '📋');
        logsBtn.title = 'View container logs';
        logsBtn.onclick = (e) => {
          e.stopPropagation();
          window.openContainerLogsModal(s.containerId, s.name);
        };
        btnRow.appendChild(logsBtn);

        // Add update button for Docker containers
        const updateBtn = el('button', 'update-btn', '⬆️');
        updateBtn.title = 'Update container to latest version';
        updateBtn.id = `update-btn-${s.id}`;
        updateBtn.onclick = (e) => {
          e.stopPropagation();
          window.updateContainer(s.containerId, s.name, s.id);
        };
        btnRow.appendChild(updateBtn);

        // Terminal exec button (subtle — visible on hover)
        const execBtn = el('button', 'exec-btn', '>_');
        execBtn.title = 'Open terminal';
        execBtn.onclick = (e) => {
          e.stopPropagation();
          if (window.openExecModal) window.openExecModal(s.containerId, s.name);
        };
        btnRow.appendChild(execBtn);
      }

      // Add logs button for services with logPath (native apps)
      if (s.logPath && !s.containerId) {
        const logsBtn = el('button', 'logs-btn', '📋');
        logsBtn.title = 'View application logs';
        logsBtn.onclick = (e) => {
          e.stopPropagation();
          window.openFileLogsModal(s.logPath, s.name);
        };
        btnRow.appendChild(logsBtn);
      }

      // Add credentials button for services that support auto-login
      if (s.isExternal || s.appTemplate || s.url) {
        const credsBtn = el('button', 'creds-btn', '🔑');
        credsBtn.title = 'Auto-login credentials';
        credsBtn.id = `creds-btn-${s.id}`;
        credsBtn.onclick = (e) => {
          e.stopPropagation();
          window.openServiceCredsModal(s);
        };
        btnRow.appendChild(credsBtn);
      }

      // Add options button for all services except 'internet'
      if (s.id !== 'internet') {
        const optBtn = el('button', 'options-btn', '⚙️');
        optBtn.title = 'Edit service settings';
        optBtn.onclick = (e) => {
          e.stopPropagation();
          window.openServiceEditModal(s);
        };
        btnRow.appendChild(optBtn);
      }

      // Add delete button for all services except Internet
      if (s.id !== 'internet') {
        const delBtn = el('button', 'delete-btn', '🗑️');
        delBtn.title = 'Delete this service';
        delBtn.onclick = (e) => {
          e.stopPropagation();
          window.deleteService(s.id, s.name);
        };
        btnRow.appendChild(delBtn);
      }

      const btn = el('button', null, 'Open');
      btn.onclick = () => window.open(serviceUrl(s.id), '_blank', 'noopener');
      btnRow.appendChild(btn);
      card.appendChild(btnRow);
      card.style.transitionDelay = `${Math.min(i * 45, 270)}ms`;

      root.appendChild(card);
    }

    requestAnimationFrame(() => {
      root.querySelectorAll('.card').forEach(card => card.classList.add('loaded'));
    });

    // Group recipe cards visually after grid is built
    if (window.groupRecipeCards) requestAnimationFrame(() => window.groupRecipeCards());

    // Refresh the service filter so the category dropdown reflects new services
    if (window.refreshServiceFilter) window.refreshServiceFilter();
  }

  function setBadge(id, up, responseTime = null) {
    const dot = document.getElementById('dot-' + id + '-grid');
    const pill = document.getElementById('badge-' + id);
    const timeEl = document.getElementById('time-' + id);
    const card = document.querySelector(`[data-app="${id}"]`);

    if (dot) {
      dot.classList.toggle('ok', up);
      dot.classList.toggle('bad', !up);
    }

    if (pill) {
      pill.textContent = up ? 'ON' : 'OFF';
      pill.classList.toggle('on', up);
      pill.classList.toggle('off', !up);
    }

    if (timeEl && responseTime !== null) {
      timeEl.textContent = up ? `${responseTime}ms` : 'timeout';
      timeEl.className = `response-time ${getResponseTimeClass(responseTime, up)}`;
    }

    // Update card status for icon coloring
    if (card) {
      card.setAttribute('data-status', up ? 'on' : 'off');
    }
  }

  async function refreshAll() {
    if (refreshInFlight) {
      refreshQueued = true;
      return refreshInFlight;
    }

    function updateStamp(label, checkedAt = new Date()) {
      const stamp = document.getElementById('stamp');
      if (stamp) stamp.textContent = `${label}: ${new Date(checkedAt).toLocaleTimeString()}`;
    }

    function applyBatchResults(statuses) {
      const dnsIds = Object.keys(SITE.dnsServers);
      dnsIds.forEach((id) => {
        const result = statuses[id];
        if (result) setQuick(id, result.isUp, result.responseTime);
      });

      if (statuses.internet) {
        setQuick('internet', statuses.internet.isUp, statuses.internet.responseTime);
      }

      window.APPS.forEach((service) => {
        const result = statuses[service.id];
        if (result) setBadge(service.id, result.isUp, result.responseTime);
      });
    }

    async function refreshFallback() {
      const dnsIds = Object.keys(SITE.dnsServers);
      const topChecks = dnsIds.map(id => checkServiceWithTiming(id));
      topChecks.push(checkServiceWithTiming('internet'));
      const topResults = await Promise.all(topChecks);

      dnsIds.forEach((id, i) => setQuick(id, topResults[i].isUp, topResults[i].responseTime));
      const internetResult = topResults[topResults.length - 1];
      setQuick('internet', internetResult.isUp, internetResult.responseTime);

      const appResults = await Promise.all(
        window.APPS.map(async s => {
          const result = await checkServiceWithTiming(s.id);
          return { id: s.id, ...result };
        })
      );

      appResults.forEach(result => {
        setBadge(result.id, result.isUp, result.responseTime);
      });
    }

    refreshInFlight = (async () => {
      try {
        const response = await fetch('/api/v1/services/status', { cache: 'no-store' });
        if (!response.ok) {
          throw new Error(`Status refresh failed (${response.status})`);
        }
        const data = await response.json();
        applyBatchResults(data.statuses || {});
        updateStamp('last check', data.checkedAt || new Date());
      } catch (batchError) {
        console.warn('Batched status refresh failed, falling back to direct probes:', batchError);
        try {
          await refreshFallback();
          updateStamp('last check');
        } catch (fallbackError) {
          console.error('Dashboard refresh failed:', fallbackError);
          updateStamp('last failed');
        }
      } finally {
        refreshInFlight = null;
        if (refreshQueued) {
          refreshQueued = false;
          setTimeout(() => { window.refreshAll(); }, 0);
        }
      }
    })();

    return refreshInFlight;
  }

  // DNS open buttons — use event delegation on .top container
  document.querySelector('.top')?.addEventListener('click', (e) => {
    const openBtn = e.target.closest('[id$="-open"]');
    if (!openBtn) return;
    const id = openBtn.id.replace('-open', '');
    if (SITE.dnsServers[id]) window.open(serviceUrl(id), '_blank', 'noopener');
  });
  document.getElementById('ca-open')?.addEventListener('click', () => window.open(serviceUrl('ca'), '_blank', 'noopener'));
  document.getElementById('creds-btn-ca')?.addEventListener('click', (e) => { e.stopPropagation(); const s = window.APPS.find(a => a.id === 'ca'); if (s && window.openServiceCredsModal) window.openServiceCredsModal(s); });
  document.getElementById('options-btn-ca')?.addEventListener('click', (e) => { e.stopPropagation(); const s = window.APPS.find(a => a.id === 'ca'); if (s && window.openServiceEditModal) window.openServiceEditModal(s); });
  document.getElementById('delete-btn-ca')?.addEventListener('click', (e) => { e.stopPropagation(); if (window.deleteService) window.deleteService('ca', 'DashCA'); });

  // Window exports
  window.loadServices = loadServices;
  window.buildGrid = buildGrid;
  window.refreshAll = refreshAll;
  window.setQuick = setQuick;
  window.setBadge = setBadge;
  window.getResponseTimeClass = getResponseTimeClass;
  window.checkServiceWithTiming = checkServiceWithTiming;
  window.serviceUrl = serviceUrl;
  window.el = el;

})();
