// App Selector System
(function () {
  const errorHandler = new ErrorHandler();
  injectModal('app-selector-modal', `<div id="app-selector-modal" class="weather-modal">
    <div class="app-selector-content">
      <h2 style="margin: 0 0 24px; color: var(--fg); text-align: center;">Choose an App</h2>
      <div id="app-selector-grid" class="app-selector-grid"></div>
      <div style="text-align: center; margin-top: 24px;">
        <button id="app-selector-cancel">Cancel</button>
      </div>
    </div>
  </div>`);

  injectModal('app-deploy-modal', `<div id="app-deploy-modal" class="weather-modal">
    <div class="weather-modal-content" style="min-width: 600px; max-width: 700px;">
      <h3 id="app-deploy-title">Deploy Application</h3>

      <div style="display: grid; grid-template-columns: 1fr; gap: 16px; margin-top: 16px;">
        <!-- Subdomain/Domain -->
        <div>
          <label for="deploy-subdomain" class="form-label-accent-sm">
            🌐 Subdomain or Domain
          </label>
          <input type="text" id="deploy-subdomain" placeholder="uptime"
                 style="width: 100%; padding: 10px; background: var(--card-bg); color: var(--fg); border: 1px solid var(--border); border-radius: 6px; font-size: 0.95rem;" />
          <div class="form-hint-sm">
            Your app will be available at: <span id="deploy-url-preview" style="color: var(--accent); font-weight: 500;">uptime.home</span>
          </div>
          <div id="subpath-compat-warning" style="display: none; padding: 8px 12px; border-radius: 6px; background: color-mix(in srgb, #ff9800 10%, var(--card-bg)); border: 1px solid color-mix(in srgb, #ff9800 30%, transparent); margin-top: 8px; font-size: 0.85rem;"></div>
        </div>

        <!-- DNS Configuration -->
        <div>
          <label class="form-label-accent">
            🗂️ DNS Configuration
          </label>
          <div class="flex-col-gap">
            <label class="radio-option">
              <input type="radio" name="dns-type" value="private" checked style="margin-right: 10px;" />
              <div>
                <div class="fw-500">Private DNS (Technitium)</div>
                <div class="text-hint">Use your local DNS server with custom TLD (.sami, .home, etc.)</div>
              </div>
            </label>
            <label class="radio-option">
              <input type="radio" name="dns-type" value="public" style="margin-right: 10px;" />
              <div>
                <div class="fw-500">Public DNS</div>
                <div class="text-hint">Use a real domain (example.com) - requires DNS provider setup</div>
              </div>
            </label>
            <label class="radio-option">
              <input type="radio" name="dns-type" value="none" style="margin-right: 10px;" />
              <div>
                <div class="fw-500">No DNS (IP:Port only)</div>
                <div class="text-hint">Access via IP address and port - no domain setup</div>
              </div>
            </label>
          </div>
        </div>

        <!-- SSL Configuration -->
        <div>
          <label class="form-label-accent">
            🔒 SSL/TLS Certificate
          </label>
          <div class="flex-col-gap">
            <label class="radio-option">
              <input type="radio" name="ssl-type" value="internal" checked style="margin-right: 10px;" />
              <div>
                <div class="fw-500">Internal CA</div>
                <div class="text-hint">Use Caddy's internal certificate authority (self-signed)</div>
              </div>
            </label>
            <label class="radio-option">
              <input type="radio" name="ssl-type" value="letsencrypt" style="margin-right: 10px;" />
              <div>
                <div class="fw-500">Let's Encrypt</div>
                <div class="text-hint">Free public SSL certificate (requires public domain)</div>
              </div>
            </label>
            <label class="radio-option">
              <input type="radio" name="ssl-type" value="none" style="margin-right: 10px;" />
              <div>
                <div class="fw-500">No SSL (HTTP only)</div>
                <div class="text-hint">⚠️ Not recommended - traffic will be unencrypted</div>
              </div>
            </label>
          </div>
        </div>

        <!-- Media Library Path (shown for media apps) -->
        <div id="media-path-section" style="display: none;">
          <label class="form-label-accent">
            📁 Media Library Location
          </label>
          <div style="display: flex; flex-direction: column; gap: 10px;">
            <!-- Detected mounts from existing media servers -->
            <div id="detected-mounts-container" style="display: none;">
              <div style="font-size: 0.85rem; color: var(--success); margin-bottom: 6px;">
                ✓ Detected from existing media servers:
              </div>
              <div id="detected-mounts-list" style="display: flex; gap: 8px; flex-wrap: wrap;"></div>
            </div>

            <!-- Path input with browse button -->
            <div class="flex-row-gap">
              <input type="text" id="deploy-media-path" placeholder="/media/Movies, /media/TVShows"
                     class="input-flex" style="font-size: 0.95rem;" />
              <button type="button" id="browse-media-btn" style="padding: 10px 16px; background: var(--accent); color: white; border: none; border-radius: 6px; cursor: pointer; font-weight: 500; white-space: nowrap;">
                📂 Browse
              </button>
            </div>

            <div id="media-path-description" class="text-hint">
              Select folders from existing servers, browse, or type paths manually. Separate multiple with commas.
            </div>

            <!-- Selected paths display -->
            <div id="selected-media-paths" style="display: none; flex-wrap: wrap; gap: 6px;"></div>
          </div>
        </div>

        <!-- Plex Claim Token (shown only for Plex deployments) -->
        <div id="plex-claim-section" style="display: none;">
          <label class="form-label-accent">
            🔑 Plex Claim Token
          </label>
          <div class="flex-row-gap">
            <input type="text" id="deploy-plex-claim" placeholder="claim-xxxxxxxxxxxxxxxxxxxx"
                   class="input-flex" style="font-size: 0.95rem;" />
            <a href="https://plex.tv/claim" target="_blank" rel="noopener noreferrer"
               style="padding: 10px 16px; background: #e5a00d; color: #000; border: none; border-radius: 6px; cursor: pointer; font-weight: 500; white-space: nowrap; text-decoration: none; display: flex; align-items: center;">
              Get Token
            </a>
          </div>
          <div style="font-size: 0.8rem; color: #e5a00d; margin-top: 4px;">
            Token expires in 4 minutes! Get it right before clicking Deploy. Leave empty to configure Plex manually later.
          </div>
        </div>

        <!-- Tailscale Security -->
        <div id="tailscale-section">
          <label class="form-label-accent">
            🔐 Tailscale Security
          </label>
          <div class="flex-col-gap">
            <label class="radio-option">
              <input type="checkbox" id="deploy-tailscale-only" style="margin-right: 10px; width: 18px; height: 18px;" />
              <div>
                <div class="fw-500">Tailscale-Only Access</div>
                <div class="text-hint">Restrict this service to Tailscale users only (100.x.x.x IPs)</div>
              </div>
            </label>
            <div id="tailscale-status" style="padding: 8px 12px; background: var(--card-bg); border-radius: 6px; font-size: 0.85rem;">
              <span style="color: var(--muted);">Checking Tailscale status...</span>
            </div>
          </div>
        </div>

        <!-- Advanced Options (Collapsible) -->
        <details>
          <summary style="cursor: pointer; color: var(--accent); font-weight: 500; margin-bottom: 8px;">⚙️ Advanced Options</summary>
          <div style="margin-top: 12px; display: grid; gap: 12px;">
            <div>
              <label for="deploy-port" style="display: block; margin-bottom: 6px;">Custom Port (optional)</label>
              <input type="number" id="deploy-port" placeholder="Leave empty for default"
                     class="form-input-card" />
            </div>
            <div>
              <label for="deploy-ip" style="display: block; margin-bottom: 6px;">Target IP Address</label>
              <input type="text" id="deploy-ip" value="localhost"
                     class="form-input-card" />
              <div class="form-hint-sm">
                Use 'localhost' for same-host containers, or specific IP for remote services
              </div>
            </div>
            <div id="volume-mounts-section" style="display: none;">
              <label class="form-label-accent-sm">📂 Volume Mounts</label>
              <div style="font-size: 0.8rem; color: var(--muted); margin-bottom: 8px;">
                Customize where container data is stored on the host. Media volumes are configured above.
              </div>
              <div id="volume-mounts-list" style="display: grid; gap: 8px;"></div>
            </div>
            <div style="margin-top: 12px;">
              <label class="form-label-accent-sm">⚙️ Resource Limits</label>
              <div style="font-size: 0.8rem; color: var(--muted); margin-bottom: 8px;">
                Optional CPU and memory constraints. Leave at 0 for unlimited.
              </div>
              <div style="display: grid; grid-template-columns: 1fr 1fr; gap: 10px;">
                <div>
                  <label for="deploy-cpu-limit" style="display: block; font-size: 0.78rem; color: var(--muted); margin-bottom: 4px;">CPU Cores</label>
                  <input type="number" id="deploy-cpu-limit" value="0" min="0" max="64" step="0.25" class="form-input-card" style="width: 100%;" placeholder="0 = unlimited" />
                </div>
                <div>
                  <label for="deploy-memory-limit" style="display: block; font-size: 0.78rem; color: var(--muted); margin-bottom: 4px;">Memory (MB)</label>
                  <input type="number" id="deploy-memory-limit" value="0" min="0" max="131072" step="64" class="form-input-card" style="width: 100%;" placeholder="0 = unlimited" />
                </div>
              </div>
            </div>
          </div>
        </details>
      </div>

      <div class="weather-modal-buttons" style="margin-top: 24px;">
        <button id="app-deploy-cancel">Cancel</button>
        <button id="app-deploy-confirm" class="btn-accent">
          🚀 Deploy
        </button>
      </div>
    </div>
  </div>`);

  const APPS_KEY = 'custom-apps';

  // Cache for API templates
  let apiTemplates = null;
  let apiCategories = null;

  const modal = document.getElementById('app-selector-modal');
  const grid = document.getElementById('app-selector-grid');

  // Fetch app templates from API
  async function fetchAppTemplates() {
    try {
      const response = await fetch('/api/v1/apps/templates');
      const data = await response.json();
      if (data.success) {
        apiTemplates = data.templates;
        apiCategories = data.categories;
        return true;
      }
    } catch (e) {
      errorHandler.logError('[AppSelector] Fetch Templates', e, { function: 'fetchApiTemplates' });
    }
    return false;
  }

  // Check port availability
  async function checkPortAvailability(port) {
    try {
      const response = await fetch(`/api/v1/apps/ports/${port}/check`);
      const data = await response.json();
      return data;
    } catch (e) {
      errorHandler.logError('[AppSelector] Check Port', e, { function: 'checkPortAvailability' });
      return { available: true }; // Assume available on error
    }
  }

  // Get suggested available port
  async function getSuggestedPort(basePort) {
    try {
      const response = await fetch(`/api/v1/apps/ports/${basePort}/suggest`);
      const data = await response.json();
      if (data.success) {
        return data.suggestedPort;
      }
    } catch (e) {
      errorHandler.logError('[AppSelector] Get Suggested Port', e, { function: 'getSuggestedPort' });
    }
    return basePort;
  }

  // Build app selector grid from API templates
  async function buildAppSelector() {
    grid.innerHTML = '<div style="text-align: center; padding: 40px; color: var(--muted);">Loading app templates...</div>';

    // Fetch templates if not cached
    if (!apiTemplates) {
      const success = await fetchAppTemplates();
      if (!success) {
        grid.innerHTML = '<div style="text-align: center; padding: 40px; color: var(--error);">Failed to load app templates. Please try again.</div>';
        return;
      }
    }

    grid.innerHTML = '';

    // Group templates by category
    const byCategory = {};
    for (const [appId, template] of Object.entries(apiTemplates)) {
      const category = template.category || 'Other';
      if (!byCategory[category]) {
        byCategory[category] = [];
      }
      byCategory[category].push({ id: appId, ...template });
    }

    // Sort categories by the order in apiCategories if available
    const categoryOrder = apiCategories ? Object.keys(apiCategories) : Object.keys(byCategory).sort();

    for (const category of categoryOrder) {
      const apps = byCategory[category];
      if (!apps || apps.length === 0) continue;

      // Sort apps by popularity (descending)
      apps.sort((a, b) => (b.popularity || 0) - (a.popularity || 0));

      // Category header with icon and color from API
      const header = document.createElement('div');
      header.className = 'app-category-header';
      const categoryInfo = apiCategories?.[category] || {};
      header.innerHTML = `${escapeHtml(categoryInfo.icon || '')} ${escapeHtml(category)}`;
      if (categoryInfo.color) {
        header.style.borderBottomColor = categoryInfo.color;
      }
      grid.appendChild(header);

      // App options
      apps.forEach(app => {
        const option = document.createElement('div');
        option.className = 'app-option';

        // Widget enabled badge
        const isWidget = app.isDashboardWidget;
        const widgetEnabled = isWidget && safeGet('widget-' + app.id + '-enabled') !== 'false';
        const widgetBadge = isWidget
          ? `<div style="font-size: 0.65rem; padding: 2px 6px; border-radius: 4px; margin-top: 4px; background: ${widgetEnabled ? '#2ecc7130' : '#e74c3c30'}; color: ${widgetEnabled ? '#2ecc71' : '#e74c3c'}; font-weight: 600;">${widgetEnabled ? 'ON' : 'OFF'}</div>`
          : '';

        // Show difficulty badge (non-widgets only)
        const difficultyBadge = !isWidget && app.difficulty ?
          `<div style="font-size: 0.65rem; padding: 2px 6px; border-radius: 4px; margin-top: 4px; background: ${
            app.difficulty === 'Easy' ? '#2ecc71' :
            app.difficulty === 'Intermediate' ? '#f39c12' : '#e74c3c'
          }20; color: ${
            app.difficulty === 'Easy' ? '#2ecc71' :
            app.difficulty === 'Intermediate' ? '#f39c12' : '#e74c3c'
          };">${escapeHtml(app.difficulty)}</div>` : '';

        option.innerHTML = `
          <div class="app-option-icon">${escapeHtml(app.icon || '📦')}</div>
          <div class="app-option-name">${escapeHtml(app.name)}</div>
          <div class="app-option-desc">${escapeHtml(app.description || '')}</div>
          ${widgetBadge}${difficultyBadge}
        `;

        if (isWidget) {
          option.onclick = () => toggleDashboardWidget(app, option);
        } else {
          option.onclick = () => showDeployConfig(app);
        }
        grid.appendChild(option);
      });
    }

    // Render recipe cards at the end of the grid
    if (window.renderRecipeCards) {
      await window.renderRecipeCards(grid);
    }
  }

  // Toggle a dashboard widget on/off
  function toggleDashboardWidget(app, optionEl) {
    const key = 'widget-' + app.id + '-enabled';
    const currentlyEnabled = safeGet(key) !== 'false';
    const newState = !currentlyEnabled;
    safeSet(key, String(newState));

    // Update visibility immediately
    const selector = app.widgetSelector;
    if (selector) {
      const el = document.querySelector(selector);
      if (el) el.style.display = newState ? '' : 'none';
    }

    // Update the badge in the app selector card
    const badge = optionEl.querySelector('div[style*="border-radius: 4px"]');
    if (badge) {
      badge.textContent = newState ? 'ON' : 'OFF';
      badge.style.background = newState ? '#2ecc7130' : '#e74c3c30';
      badge.style.color = newState ? '#2ecc71' : '#e74c3c';
    }

    showNotification(`${app.name} widget ${newState ? 'enabled' : 'disabled'}`, 'success', 2000);
  }

  // Show deployment configuration modal
  async function showDeployConfig(appTemplate) {
    const deployModal = document.getElementById('app-deploy-modal');
    const title = document.getElementById('app-deploy-title');
    const subdomainInput = document.getElementById('deploy-subdomain');
    const urlPreview = document.getElementById('deploy-url-preview');
    const ipInput = document.getElementById('deploy-ip');
    const portInput = document.getElementById('deploy-port');
    const tailscaleCheckbox = document.getElementById('deploy-tailscale-only');
    const tailscaleStatus = document.getElementById('tailscale-status');

    // Check for existing container with same image
    try {
      const checkResponse = await secureFetch('/api/v1/apps/check-existing', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ appId: appTemplate.id })
      });
      const checkResult = await checkResponse.json();

      if (checkResult.success && checkResult.exists) {
        const container = checkResult.container;
        const useExisting = confirm(
          `Found existing ${appTemplate.name} container:\n\n` +
          `Container: ${container.name}\n` +
          `Status: ${container.status}\n` +
          `Port: ${container.primaryPort || 'N/A'}\n\n` +
          `Would you like to use this existing container?\n\n` +
          `Click OK to configure DNS/Caddy for the existing container.\n` +
          `Click Cancel to deploy a new container.`
        );

        if (useExisting) {
          // Store existing container info for deployment
          appTemplate._useExisting = true;
          appTemplate._existingContainer = container;
        }
      }
    } catch (e) {
      // Ignore container check errors
    }

    // Set title with app info
    title.textContent = `Deploy ${appTemplate.name}`;

    // Pre-fill subdomain from template or app ID
    const defaultSubdomain = appTemplate.subdomain || appTemplate.id.replace(/-/g, '');
    subdomainInput.value = defaultSubdomain;

    // Show subpath compatibility warning in subdirectory mode
    const subpathWarning = document.getElementById('subpath-compat-warning');
    if (subpathWarning) {
      if (SITE.routingMode === 'subdirectory') {
        const support = appTemplate.subpathSupport || 'strip';
        if (support === 'none') {
          subpathWarning.style.display = 'block';
          subpathWarning.innerHTML = '<span style="color: #ff9800;">&#9888; <strong>' + appTemplate.name + '</strong> does not support subdirectory mode. It may not work correctly at a subpath.</span>';
        } else if (support === 'strip') {
          subpathWarning.style.display = 'block';
          subpathWarning.innerHTML = '<span style="color: var(--muted);">&#9432; ' + appTemplate.name + ' has unverified subdirectory support. It may require additional configuration.</span>';
        } else {
          subpathWarning.style.display = 'none';
        }
      } else {
        subpathWarning.style.display = 'none';
      }
    }

    // Pre-select DNS/SSL from site config (set during setup wizard)
    const cfgDnsType = SITE.defaults.dnsType || (SITE.configurationType === 'public' ? 'public' : 'private');
    const cfgSslType = SITE.defaults.sslType || (SITE.configurationType === 'public' ? 'letsencrypt' : 'internal');
    const dnsRadio = document.querySelector(`input[name="dns-type"][value="${cfgDnsType}"]`);
    const sslRadio = document.querySelector(`input[name="ssl-type"][value="${cfgSslType}"]`);
    if (dnsRadio) dnsRadio.checked = true;
    else document.querySelector('input[name="dns-type"][value="private"]').checked = true;
    if (sslRadio) sslRadio.checked = true;
    else document.querySelector('input[name="ssl-type"][value="internal"]').checked = true;
    ipInput.value = SITE.defaults.targetIP || 'localhost';
    tailscaleCheckbox.checked = false;

    // Move DNS/SSL into Advanced section when already configured
    const dnsSection = document.querySelector('#app-deploy-modal .flex-col-gap')?.closest('div');
    const advancedDetails = document.querySelector('#app-deploy-modal details');
    const advancedContent = advancedDetails?.querySelector('div');
    if (advancedDetails && advancedContent && (SITE.configurationType === 'public' || SITE.configurationType === 'homelab')) {
      // Move DNS and SSL sections inside Advanced
      const dnsSectionEl = document.querySelectorAll('#app-deploy-modal input[name="dns-type"]')[0]?.closest('div.flex-col-gap')?.parentElement;
      const sslSectionEl = document.querySelectorAll('#app-deploy-modal input[name="ssl-type"]')[0]?.closest('div.flex-col-gap')?.parentElement;
      if (dnsSectionEl && !dnsSectionEl.dataset.moved) {
        advancedContent.appendChild(dnsSectionEl);
        dnsSectionEl.dataset.moved = '1';
      }
      if (sslSectionEl && !sslSectionEl.dataset.moved) {
        advancedContent.appendChild(sslSectionEl);
        sslSectionEl.dataset.moved = '1';
      }
    }

    // Handle media path section for media apps
    const mediaPathSection = document.getElementById('media-path-section');
    const mediaPathInput = document.getElementById('deploy-media-path');
    const mediaPathDescription = document.getElementById('media-path-description');

    if (appTemplate.mediaMount) {
      mediaPathSection.style.display = 'block';
      mediaPathInput.value = '';
      mediaPathInput.placeholder = '/media/Movies, /media/TVShows or click Browse';

      // Fetch detected mounts from existing media servers
      const detectedMountsContainer = document.getElementById('detected-mounts-container');
      const detectedMountsList = document.getElementById('detected-mounts-list');

      try {
        const mountsResponse = await fetch('/api/v1/media/detected-mounts');
        const mountsResult = await mountsResponse.json();

        if (mountsResult.success && mountsResult.mounts.length > 0) {
          detectedMountsContainer.style.display = 'block';
          detectedMountsList.innerHTML = '';

          // Auto-fill media path with all detected mounts
          const autoPaths = [...new Set(mountsResult.mounts.map(m => m.hostPath))];
          mediaPathInput.value = autoPaths.join(', ');

          mountsResult.mounts.forEach(mount => {
            const btn = document.createElement('button');
            btn.type = 'button';
            const isSelected = autoPaths.includes(mount.hostPath);
            btn.style.cssText = `padding: 8px 14px; font-size: 0.85rem; background: color-mix(in srgb, var(--success) ${isSelected ? '40%' : '15%'}, var(--card-bg)); border: 1px solid var(--success); border-radius: 6px; cursor: pointer; color: var(--fg);`;
            btn.innerHTML = `<span style="font-weight: 500;">${escapeHtml(mount.folderName)}</span><br><span style="font-size: 0.7rem; color: var(--muted);">from ${escapeHtml(mount.sourceImage)}</span>`;
            btn.title = `${mount.hostPath} (from ${mount.sourceContainer})`;
            btn.onclick = () => {
              const currentPaths = mediaPathInput.value.split(',').map(p => p.trim()).filter(p => p);
              const idx = currentPaths.indexOf(mount.hostPath);
              if (idx >= 0) {
                currentPaths.splice(idx, 1);
                btn.style.background = 'color-mix(in srgb, var(--success) 15%, var(--card-bg))';
              } else {
                currentPaths.push(mount.hostPath);
                btn.style.background = 'color-mix(in srgb, var(--success) 40%, var(--card-bg))';
              }
              mediaPathInput.value = currentPaths.join(', ');
            };
            detectedMountsList.appendChild(btn);
          });
        } else {
          detectedMountsContainer.style.display = 'none';
        }
      } catch (e) {
        detectedMountsContainer.style.display = 'none';
      }

      // Set up browse button
      document.getElementById('browse-media-btn').onclick = () => {
        openFolderBrowser(mediaPathInput);
      };
    } else {
      mediaPathSection.style.display = 'none';
      mediaPathInput.value = '';
      document.getElementById('detected-mounts-container').style.display = 'none';
    }

    // Show Plex claim token section for Plex deployments
    const plexClaimSection = document.getElementById('plex-claim-section');
    if (plexClaimSection) {
      if (appTemplate.id === 'plex' || appTemplate.claimToken) {
        plexClaimSection.style.display = 'block';
        document.getElementById('deploy-plex-claim').value = '';
      } else {
        plexClaimSection.style.display = 'none';
      }
    }

    // Populate volume mounts in Advanced Options
    const volumeSection = document.getElementById('volume-mounts-section');
    const volumeList = document.getElementById('volume-mounts-list');
    volumeList.innerHTML = '';

    if (appTemplate.docker?.volumes?.length) {
      const mediaContainerPath = appTemplate.mediaMount?.containerPath;
      const nonMediaVolumes = appTemplate.docker.volumes.filter(v => !v.includes('{{MEDIA_PATH}}') && !(mediaContainerPath && v.endsWith(':' + mediaContainerPath)));

      if (nonMediaVolumes.length > 0) {
        volumeSection.style.display = 'block';
        nonMediaVolumes.forEach((vol, i) => {
          const [hostDefault, containerPath] = vol.split(':');
          const row = document.createElement('div');
          row.style.cssText = 'display: flex; gap: 6px; align-items: center;';
          row.innerHTML = `
            <input type="text" class="vol-host-path" data-container-path="${containerPath}" value="${hostDefault}"
                   style="flex: 1; padding: 8px; background: var(--card-bg); color: var(--fg); border: 1px solid var(--border); border-radius: 6px; font-size: 0.85rem;" />
            <span style="color: var(--muted); font-size: 0.85rem; white-space: nowrap;">→ ${containerPath}</span>
            <button type="button" class="vol-browse-btn" style="padding: 8px 10px; background: var(--accent); color: white; border: none; border-radius: 6px; cursor: pointer; font-size: 0.8rem;">📂</button>
          `;
          volumeList.appendChild(row);
          row.querySelector('.vol-browse-btn').onclick = () => {
            const input = row.querySelector('.vol-host-path');
            openFolderBrowser(input);
          };
        });
      } else {
        volumeSection.style.display = 'none';
      }
    } else {
      volumeSection.style.display = 'none';
    }

    // Set default port from template and check availability
    const defaultPort = appTemplate.defaultPort || 8080;
    portInput.value = '';
    portInput.placeholder = `Default: ${defaultPort}`;

    // Add port status element if not exists
    let portStatus = document.getElementById('deploy-port-status');
    if (!portStatus) {
      portStatus = document.createElement('div');
      portStatus.id = 'deploy-port-status';
      portStatus.style.cssText = 'font-size: 0.8rem; margin-top: 4px;';
      portInput.parentNode.appendChild(portStatus);
    }

    // Check default port availability
    async function checkAndUpdatePortStatus() {
      const portToCheck = portInput.value || defaultPort;
      portStatus.innerHTML = '<span style="color: var(--muted);">Checking port...</span>';

      const result = await checkPortAvailability(portToCheck);
      if (result.available) {
        portStatus.innerHTML = `<span style="color: #4caf50;">Port ${escapeHtml(String(portToCheck))} is available</span>`;
      } else {
        const suggestedPort = await getSuggestedPort(defaultPort);
        portStatus.innerHTML = `
          <span style="color: #e74c3c;">Port ${escapeHtml(portToCheck)} in use by ${escapeHtml(result.conflict?.usedBy || 'unknown')}</span>
        `;
        const useBtn = document.createElement('button');
        useBtn.type = 'button';
        useBtn.textContent = `Use ${suggestedPort}`;
        useBtn.style.cssText = 'margin-left: 8px; padding: 2px 8px; font-size: 0.75rem; cursor: pointer;';
        useBtn.onclick = () => {
          document.getElementById('deploy-port').value = suggestedPort;
          portStatus.innerHTML = `<span style="color: #4caf50;">Using suggested port ${escapeHtml(String(suggestedPort))}</span>`;
        };
        portStatus.appendChild(useBtn);
      }
    }

    // Check port on input change (debounced)
    let portCheckTimeout;
    portInput.oninput = function() {
      clearTimeout(portCheckTimeout);
      portCheckTimeout = setTimeout(checkAndUpdatePortStatus, 500);
    };

    // Initial port check
    checkAndUpdatePortStatus();

    // Fetch Tailscale status
    try {
      const response = await fetch('/api/v1/tailscale/status');
      const data = await response.json();
      if (data.success && data.installed && data.connected) {
        tailscaleStatus.innerHTML = `
          <span style="color: #4caf50;">Connected</span>
          <span style="color: var(--muted); margin-left: 8px;">${data.self?.hostname} (${data.self?.ip})</span>
          <span style="color: var(--muted); margin-left: 8px;">| ${data.deviceCount} devices</span>
        `;
      } else if (data.installed) {
        tailscaleStatus.innerHTML = `<span style="color: #ff9800;">Not connected</span>`;
      } else {
        tailscaleStatus.innerHTML = `<span style="color: var(--muted);">Not available</span>`;
        tailscaleCheckbox.disabled = true;
      }
    } catch (e) {
      tailscaleStatus.innerHTML = `<span style="color: var(--muted);">Could not check status</span>`;
    }

    // Update URL preview in real-time
    function updateUrlPreview() {
      const subdomain = subdomainInput.value || 'subdomain';
      const dnsType = document.querySelector('input[name="dns-type"]:checked').value;
      const sslType = document.querySelector('input[name="ssl-type"]:checked').value;

      let url = '';
      if (SITE.routingMode === 'subdirectory' && SITE.domain) {
        // Subdirectory mode: domain.com/app
        url = `https://${SITE.domain}/${subdomain}`;
      } else if (dnsType === 'private') {
        const protocol = sslType === 'none' ? 'http' : 'https';
        url = `${protocol}://${buildDomain(subdomain)}`;
      } else if (dnsType === 'public') {
        const protocol = sslType === 'none' ? 'http' : 'https';
        const domain = SITE.domain || subdomain;
        url = SITE.domain ? `${protocol}://${subdomain}.${SITE.domain}` : `${protocol}://${subdomain}`;
      } else {
        const port = portInput.value || appTemplate.defaultPort || DC.DEFAULTS.SERVICE_PORT;
        url = `http://${ipInput.value}:${port}`;
      }
      urlPreview.textContent = url;
    }

    // Attach listeners
    subdomainInput.oninput = updateUrlPreview;
    ipInput.oninput = updateUrlPreview;
    portInput.oninput = updateUrlPreview;
    document.querySelectorAll('input[name="dns-type"]').forEach(radio => {
      radio.onchange = updateUrlPreview;
    });
    document.querySelectorAll('input[name="ssl-type"]').forEach(radio => {
      radio.onchange = updateUrlPreview;
    });

    updateUrlPreview();

    // Close app selector, open deploy config
    modal.classList.remove('show');
    deployModal.classList.add('show');

    // Store app template for deployment
    deployModal.dataset.appTemplate = JSON.stringify(appTemplate);
  }

  // Add app to grid with full Docker deployment
  async function addAppToGrid(deployConfig) {
    const appTemplate = deployConfig.appTemplate;
    const customApps = safeGetJSON(APPS_KEY, []);

    // Check if using existing container
    const usingExisting = appTemplate._useExisting && appTemplate._existingContainer;

    // Check if app already exists - skip if using existing container (user already confirmed)
    const existingApp = customApps.find(a => a.id === deployConfig.subdomain);
    if (existingApp && !usingExisting) {
      const confirmed = confirm(`An app with subdomain "${deployConfig.subdomain}" already exists. Redeploy?`);
      if (!confirmed) return;
    }
    // Remove from localStorage to allow redeployment/update
    if (existingApp) {
      const index = customApps.indexOf(existingApp);
      customApps.splice(index, 1);
      safeSet(APPS_KEY, JSON.stringify(customApps));
    }

    // Check port availability before deployment (skip if using existing container)
    if (!usingExisting) {
      const portToUse = deployConfig.port || appTemplate.defaultPort || 8080;
      showNotification(`Checking port ${portToUse} availability...`, 'info', 0);

      const portCheck = await checkPortAvailability(portToUse);
      if (!portCheck.available) {
        const suggestedPort = await getSuggestedPort(appTemplate.defaultPort || 8080);
        const useAlternate = confirm(
          `Port ${portToUse} is already in use by ${portCheck.conflict?.usedBy || 'another container'}.\n\n` +
          `Would you like to use port ${suggestedPort} instead?`
        );
        if (useAlternate) {
          deployConfig.port = suggestedPort;
        } else {
          showNotification('Deployment cancelled - port conflict', 'error', 5000);
          return;
        }
      }
    } else {
      // Use existing container's port
      deployConfig.port = appTemplate._existingContainer.primaryPort;
    }
    showNotification(
      usingExisting
        ? `Configuring ${appTemplate.name} with existing container...`
        : `Deploying ${appTemplate.name}...`,
      'info', 0
    );

    try {
      // Prepare deployment config from user's choices
      const apiDeployConfig = {
        appId: appTemplate.id,
        config: {
          subdomain: deployConfig.subdomain,
          ip: deployConfig.ip,
          createDns: deployConfig.dnsType === 'private',  // Only create DNS for private
          port: deployConfig.port || appTemplate.defaultPort || null,  // Use custom, template default, or null
          sslType: deployConfig.sslType,
          dnsType: deployConfig.dnsType,
          tailscaleOnly: deployConfig.tailscaleOnly || false,  // Tailscale-only access restriction
          mediaPath: deployConfig.mediaPath || null,  // Media folder path for media apps
          plexClaimToken: deployConfig.plexClaimToken || null,  // Plex claim token for auto-claim
          customVolumes: deployConfig.customVolumes || null  // Custom volume mount overrides
        }
      };

      // Add existing container info if using existing
      if (usingExisting) {
        apiDeployConfig.config.useExisting = true;
        apiDeployConfig.config.existingContainerId = appTemplate._existingContainer.id;
        apiDeployConfig.config.existingPort = appTemplate._existingContainer.primaryPort;
        // Use existing container's port if no custom port specified
        if (!deployConfig.port && appTemplate._existingContainer.primaryPort) {
          apiDeployConfig.config.port = appTemplate._existingContainer.primaryPort;
        }
      }

      // Call deployment API
      const response = await secureFetch('/api/v1/apps/deploy', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(apiDeployConfig)
      });

      const result = await response.json();

      if (result.success) {
        // Add to saved apps (store IP for later deletion)
        const newApp = {
          id: deployConfig.subdomain,  // Use subdomain as ID
          name: appTemplate.name,
          logo: `/assets/${appTemplate.id}.png`,
          containerId: result.containerId,
          url: result.url,
          ip: deployConfig.ip,  // Store IP for DNS record deletion
          appTemplate: appTemplate.id,  // Store original template ID
          tailscaleOnly: deployConfig.tailscaleOnly || false  // Tailscale protection status
        };
        customApps.push(newApp);
        safeSet(APPS_KEY, JSON.stringify(customApps));

        // Add to APPS array and rebuild grid
        // Access APPS from parent scope via window
        if (window.APPS && !window.APPS.some(a => a.id === appTemplate.id)) {
          window.APPS.push(newApp);
          if (typeof window.buildGrid === 'function') {
            window.buildGrid();
          }
          if (typeof window.refreshAll === 'function') {
            setTimeout(() => window.refreshAll(), 500);
          }
        }

        // Show success with URL (and warning if DNS failed)
        let message = result.usedExisting
          ? `${appTemplate.name} configured with existing container!\nURL: ${result.url}`
          : `${appTemplate.name} deployed successfully!\nURL: ${result.url}`;
        if (result.warning) {
          message += `\n\n⚠ Warning: ${result.warning}`;
        }

        showNotification(message, 'success', 8000);

        // Clean up temporary properties
        delete appTemplate._useExisting;
        delete appTemplate._existingContainer;
        
        // For HTTPS URLs, check SSL certificate status
        if (result.url && result.url.startsWith('https://')) {
          checkSSLCertificate(result.url, appTemplate.name);
        }

        // Show setup instructions if available
        if (result.setupInstructions && result.setupInstructions.length > 0) {
          setTimeout(() => {
            const instructions = result.setupInstructions.join('\n');
            showNotification(`Setup Instructions for ${appTemplate.name}: ${instructions}`, 'info', 10000);
          }, 1000);
        }
      } else {
        throw new Error(result.error || 'Deployment failed');
      }
    } catch (error) {
      errorHandler.logError('[AppSelector] Deployment', error, { function: 'deploy' });
      showNotification(
        `Failed to deploy ${appTemplate.name}: ${error.message}`,
        'error',
        8000
      );
    }
  }

  // Check SSL certificate status and notify when ready
  async function checkSSLCertificate(url, appName) {
    showNotification(`⏳ Generating SSL certificate for ${appName}...`, 'warning', 60000);
    
    let attempts = 0;
    const maxAttempts = 12; // 60 seconds total (5 second intervals)
    
    const checkCert = async () => {
      attempts++;
      
      try {
        // Try to fetch the URL - if SSL works, this will succeed
        const response = await fetch(url, { 
          method: 'HEAD',
          mode: 'no-cors' // Avoid CORS issues
        });
        
        // If we get here, SSL is working
        showNotification(`✅ ${appName} is ready! SSL certificate generated.`, 'success', 5000);
        return true;
      } catch (error) {
        // SSL not ready yet
        if (attempts < maxAttempts) {
          setTimeout(checkCert, 5000); // Check again in 5 seconds
        } else {
          showNotification(
            `⚠️ ${appName} deployed but SSL certificate may still be generating.\nTry refreshing in a moment if you see a certificate error.`,
            'warning',
            10000
          );
        }
        return false;
      }
    };
    
    // Start checking after 3 seconds (give Caddy time to start)
    setTimeout(checkCert, 3000);
  }

  // Load custom apps on startup
  function loadCustomApps() {
    const customApps = safeGetJSON(APPS_KEY, []);
    customApps.forEach(app => {
      if (!window.APPS.some(a => a.id === app.id)) {
        window.APPS.push(app);
      }
    });
  }

  // Event listeners
  document.getElementById('add-service-btn')?.addEventListener('click', () => {
    buildAppSelector();
    modal.classList.add('show');
  });

  wireModal(modal, document.getElementById('app-selector-cancel'));

  // Deploy modal event listeners
  const deployModal = document.getElementById('app-deploy-modal');

  document.getElementById('app-deploy-cancel')?.addEventListener('click', () => {
    deployModal.classList.remove('show');
  });

  document.getElementById('app-deploy-confirm')?.addEventListener('click', () => {
    // Get user configuration
    const appTemplate = JSON.parse(deployModal.dataset.appTemplate);
    const mediaPath = document.getElementById('deploy-media-path').value.trim();

    // Collect custom volume overrides
    const customVolumes = [];
    document.querySelectorAll('#volume-mounts-list .vol-host-path').forEach(input => {
      customVolumes.push({ hostPath: input.value.trim(), containerPath: input.dataset.containerPath });
    });

    const deployConfig = {
      appTemplate: appTemplate,
      subdomain: document.getElementById('deploy-subdomain').value.trim(),
      dnsType: document.querySelector('input[name="dns-type"]:checked').value,
      sslType: document.querySelector('input[name="ssl-type"]:checked').value,
      ip: document.getElementById('deploy-ip').value.trim(),
      port: document.getElementById('deploy-port').value.trim(),
      tailscaleOnly: document.getElementById('deploy-tailscale-only').checked,
      mediaPath: mediaPath || null,
      plexClaimToken: document.getElementById('deploy-plex-claim')?.value.trim() || null,
      customVolumes: customVolumes.length > 0 ? customVolumes : null,
      resources: {
        cpus: parseFloat(document.getElementById('deploy-cpu-limit').value) || 0,
        memory: parseFloat(document.getElementById('deploy-memory-limit').value) || 0,
      }
    };

    // Validate subdomain
    if (!deployConfig.subdomain) {
      showNotification('Please enter a subdomain or domain name', 'warning');
      return;
    }

    // Validate media path for media apps
    if (appTemplate.mediaMount?.required && !mediaPath) {
      showNotification('Please enter a media library path for this application', 'warning');
      return;
    }

    // Close deploy modal
    deployModal.classList.remove('show');

    // Start deployment
    addAppToGrid(deployConfig);
  });

  wireModal(deployModal);

  // ===== FOLDER BROWSER FUNCTIONALITY =====
  const folderBrowserModal = document.getElementById('folder-browser-modal');
  const folderBrowserPath = document.getElementById('folder-browser-path');
  const folderBrowserList = document.getElementById('folder-browser-list');
  const folderBrowserSelected = document.getElementById('folder-browser-selected');
  const folderBrowserSelectedList = document.getElementById('folder-browser-selected-list');
  let currentBrowserPath = '';
  let selectedFolders = [];
  let targetMediaInput = null;

  window.openFolderBrowser = function(mediaInput) {
    targetMediaInput = mediaInput;
    selectedFolders = mediaInput.value.split(',').map(p => p.trim()).filter(p => p);
    currentBrowserPath = '';
    updateSelectedFoldersDisplay();
    loadFolderContents('');
    folderBrowserModal.classList.add('show');
  };

  async function loadFolderContents(path) {
    folderBrowserPath.textContent = path || 'Select a drive...';
    folderBrowserList.innerHTML = '<div style="padding: 20px; text-align: center; color: var(--muted);">Loading...</div>';

    try {
      const response = await fetch(`/api/v1/browse/directories?path=${encodeURIComponent(path)}`);
      const result = await response.json();

      if (!result.success) {
        folderBrowserList.innerHTML = `<div style="padding: 20px; text-align: center; color: var(--error);">Error: ${escapeHtml(result.error)}</div>`;
        return;
      }

      currentBrowserPath = result.path || '';
      folderBrowserPath.textContent = currentBrowserPath || 'Select a drive...';

      let html = '';

      // Add parent folder navigation
      if (result.parent && result.parent !== result.path) {
        html += `<div class="folder-item" data-path="${escapeHtml(result.parent)}" style="padding: 12px 16px; border-bottom: 1px solid var(--border); cursor: pointer; display: flex; align-items: center; gap: 10px;">
          <span style="font-size: 1.2rem;">⬆️</span>
          <span style="color: var(--muted);">.. Parent Directory</span>
        </div>`;
      }

      // Add folders
      if (result.items.length === 0 && !result.parent) {
        html += '<div style="padding: 20px; text-align: center; color: var(--muted);">No browseable drives configured. Check your docker-compose.yml volume mounts.</div>';
      } else if (result.items.length === 0) {
        html += '<div style="padding: 20px; text-align: center; color: var(--muted);">No subfolders found</div>';
      } else {
        result.items.forEach(item => {
          const icon = item.type === 'drive' ? '💾' : '📁';
          const isSelected = selectedFolders.includes(item.path);
          const selectedStyle = isSelected ? 'background: color-mix(in srgb, var(--success) 20%, transparent);' : '';
          html += `<div class="folder-item" data-path="${escapeHtml(item.path)}" style="padding: 12px 16px; border-bottom: 1px solid var(--border); cursor: pointer; display: flex; align-items: center; gap: 10px; ${selectedStyle}">
            <span style="font-size: 1.2rem;">${icon}</span>
            <span style="flex: 1;">${escapeHtml(item.name)}</span>
            ${isSelected ? '<span style="color: var(--success);">✓</span>' : ''}
          </div>`;
        });
      }

      folderBrowserList.innerHTML = html;

      // Add click handlers
      folderBrowserList.querySelectorAll('.folder-item').forEach(item => {
        item.addEventListener('click', () => {
          loadFolderContents(item.dataset.path);
        });
        item.addEventListener('mouseenter', () => {
          item.style.background = 'var(--card-bg)';
        });
        item.addEventListener('mouseleave', () => {
          const isSelected = selectedFolders.includes(item.dataset.path);
          item.style.background = isSelected ? 'color-mix(in srgb, var(--success) 20%, transparent)' : '';
        });
      });

    } catch (error) {
      folderBrowserList.innerHTML = `<div style="padding: 20px; text-align: center; color: var(--error);">Failed to load: ${escapeHtml(error.message)}</div>`;
    }
  }

  function updateSelectedFoldersDisplay() {
    if (selectedFolders.length === 0) {
      folderBrowserSelected.style.display = 'none';
      return;
    }
    folderBrowserSelected.style.display = 'block';
    folderBrowserSelectedList.innerHTML = selectedFolders.map(path => `
      <span style="padding: 6px 12px; background: var(--card-bg); border: 1px solid var(--success); border-radius: 4px; display: flex; align-items: center; gap: 6px;">
        ${escapeHtml(path)}
        <button type="button" onclick="removeSelectedFolder('${escapeHtml(path)}')" style="background: none; border: none; cursor: pointer; color: var(--error); font-size: 1rem; padding: 0;">×</button>
      </span>
    `).join('');
  }

  window.removeSelectedFolder = function(path) {
    selectedFolders = selectedFolders.filter(p => p !== path);
    updateSelectedFoldersDisplay();
    loadFolderContents(currentBrowserPath); // Refresh to update checkmarks
  };

  document.getElementById('folder-browser-select-current').addEventListener('click', () => {
    if (currentBrowserPath && !selectedFolders.includes(currentBrowserPath)) {
      selectedFolders.push(currentBrowserPath);
      updateSelectedFoldersDisplay();
      loadFolderContents(currentBrowserPath); // Refresh to show checkmark
    }
  });

  wireModal(folderBrowserModal, document.getElementById('folder-browser-cancel'));

  document.getElementById('folder-browser-done').addEventListener('click', () => {
    if (targetMediaInput) {
      targetMediaInput.value = selectedFolders.join(', ');
    }
    folderBrowserModal.classList.remove('show');
  });

  // Load custom apps on page load
  loadCustomApps();
})();
