const express = require('express');
const { APP_PORTS, ARR_SERVICES } = require('../../src/utilities/constants');
const { ok } = require('../../src/utils/responses');

/**
 * Arr service detection routes factory
 * @param {Object} deps - Explicit dependencies
 * @param {Object} deps.docker - Docker client wrapper
 * @param {Object} deps.servicesStateManager - Services state manager
 * @param {Object} deps.credentialManager - Credential manager
 * @param {Function} deps.fetchT - Timeout-wrapped fetch
 * @param {Function} deps.asyncHandler - Async route handler wrapper
 * @param {Object} deps.helpers - Arr helpers module
 * @returns {express.Router}
 */
module.exports = function({ docker, servicesStateManager, credentialManager, fetchT, asyncHandler, helpers }) {
  const router = express.Router();

  // Detect running arr services and their configurations
  router.get('/arr/detect', asyncHandler(async (req, res) => {
    const containers = await docker.client.listContainers({ all: false });
    const detected = {
      plex: null,
      radarr: null,
      sonarr: null,
      overseerr: null,
      lidarr: null,
      prowlarr: null
    };

    // Service detection patterns
    const servicePatterns = ARR_SERVICES;

    for (const container of containers) {
      const containerName = container.Names[0]?.replace(/^\//, '').toLowerCase() || '';
      const image = container.Image.toLowerCase();

      for (const [service, config] of Object.entries(servicePatterns)) {
        if (config.names.some(n => containerName.includes(n) || image.includes(n))) {
          // Find the exposed port
          const portInfo = container.Ports.find(p => p.PrivatePort === config.port);
          const exposedPort = portInfo?.PublicPort || config.port;

          detected[service] = {
            containerId: container.Id,
            containerName: container.Names[0]?.replace(/^\//, ''),
            image: container.Image,
            port: exposedPort,
            status: container.State,
            url: helpers.getServiceUrl(containerName, exposedPort)
          };

          // Get API key for arr services (not Plex or Overseerr)
          if (['radarr', 'sonarr', 'lidarr', 'prowlarr'].includes(service)) {
            detected[service].apiKey = await helpers.getArrApiKey(containerName);
          }
        }
      }
    }

    // Get Plex token if Plex is detected
    if (detected.plex) {
      detected.plex.token = await helpers.getPlexToken(detected.plex.containerName);
    }

    ok(res, {
      services: detected,
      summary: {
        plexReady: !!(detected.plex?.token),
        radarrReady: !!(detected.radarr?.apiKey),
        sonarrReady: !!(detected.sonarr?.apiKey),
        overseerrRunning: !!detected.overseerr
      }
    });
  }, 'arr-detect'));

  // Smart Detect: Unified discovery of all arr services
  router.get('/arr/smart-detect', asyncHandler(async (req, res) => {
    const serviceList = ['plex', 'radarr', 'sonarr', 'prowlarr', 'seerr'];
    const defaultPorts = APP_PORTS;
    const result = {};

    // 1. Scan Docker containers
    let containers = [];
    try { containers = await docker.client.listContainers({ all: false }); } catch (e) { /* Docker not available */ }

    const servicePatterns = ARR_SERVICES;

    const dockerDetected = {};
    for (const container of containers) {
      const containerName = container.Names[0]?.replace(/^\//, '').toLowerCase() || '';
      const image = container.Image.toLowerCase();
      for (const [svc, config] of Object.entries(servicePatterns)) {
        if (config.names.some(n => containerName.includes(n) || image.includes(n))) {
          const portInfo = container.Ports.find(p => p.PrivatePort === config.port);
          dockerDetected[svc] = {
            containerId: container.Id,
            containerName: container.Names[0]?.replace(/^\//, ''),
            port: portInfo?.PublicPort || config.port,
            status: container.State
          };
        }
      }
    }

    // 2. Load services.json for external entries
    let storedServices = [];
    try {
      const data = await servicesStateManager.read();
      storedServices = Array.isArray(data) ? data : data.services || [];
    } catch (e) { /* ignore */ }

    // 3. Load stored credentials
    const storedCreds = {};
    const seedboxBaseUrl = await credentialManager.retrieve('arr.seedbox.baseurl');

    for (const svc of serviceList) {
      const credKey = svc === 'plex' ? 'arr.plex.token' : `arr.${svc}.apikey`;
      const apiKey = await credentialManager.retrieve(credKey);
      const metadata = await credentialManager.getMetadata(credKey);
      if (apiKey) {
        storedCreds[svc] = { apiKey, metadata };
      }
    }

    // 4. Build detection result for each service
    for (const svc of serviceList) {
      const entry = {
        status: 'not_found',
        source: null,
        url: null,
        hasApiKey: false,
        hasToken: false,
        containerId: null,
        containerName: null,
        version: null
      };

      // Check Docker first
      if (dockerDetected[svc]) {
        const dc = dockerDetected[svc];
        entry.containerId = dc.containerId;
        entry.containerName = dc.containerName;
        entry.source = 'local';
        entry.url = `http://localhost:${dc.port}`;

        if (svc === 'plex') {
          // Try to get Plex token from container
          try {
            const token = await helpers.getPlexToken(dc.containerName);
            if (token) {
              entry.hasToken = true;
              entry.status = 'connected';
              // Store for later use
              await credentialManager.store('arr.plex.token', token, {
                service: 'plex', source: 'local', url: entry.url,
                lastVerified: new Date().toISOString()
              });
            } else {
              entry.status = 'needs_key';
            }
          } catch (e) { entry.status = 'needs_key'; }
        } else if (svc === 'seerr') {
          entry.status = 'connected';
          // Check what Overseerr has configured using Plex-based session auth
          try {
            const session = await helpers.getOverseerrSession();
            if (session) {
              entry.hasApiKey = true;
              const configuredServices = { radarr: false, sonarr: false, plex: false };
              try {
                const radarrCheck = await fetchT(`http://host.docker.internal:${dc.port}/api/v1/settings/radarr`, {
                  headers: { 'Cookie': session.cookie },
                  signal: AbortSignal.timeout(5000)
                });
                if (radarrCheck.ok) {
                  const radarrSettings = await radarrCheck.json();
                  configuredServices.radarr = Array.isArray(radarrSettings) ? radarrSettings.length > 0 : !!radarrSettings;
                }
              } catch (e) { /* ignore */ }
              try {
                const sonarrCheck = await fetchT(`http://host.docker.internal:${dc.port}/api/v1/settings/sonarr`, {
                  headers: { 'Cookie': session.cookie },
                  signal: AbortSignal.timeout(5000)
                });
                if (sonarrCheck.ok) {
                  const sonarrSettings = await sonarrCheck.json();
                  configuredServices.sonarr = Array.isArray(sonarrSettings) ? sonarrSettings.length > 0 : !!sonarrSettings;
                }
              } catch (e) { /* ignore */ }
              try {
                const plexCheck = await fetchT(`http://host.docker.internal:${dc.port}/api/v1/settings/plex`, {
                  headers: { 'Cookie': session.cookie },
                  signal: AbortSignal.timeout(5000)
                });
                if (plexCheck.ok) {
                  const plexSettings = await plexCheck.json();
                  configuredServices.plex = !!plexSettings?.ip;
                }
              } catch (e) { /* ignore */ }
              entry.configuredServices = configuredServices;
            }
          } catch (e) { /* ignore */ }
        } else {
          // arr services - try to get API key from container
          try {
            const key = await helpers.getArrApiKey(dc.containerName);
            if (key) {
              entry.hasApiKey = true;
              entry.status = 'connected';
            } else {
              entry.status = storedCreds[svc] ? 'connected' : 'needs_key';
              entry.hasApiKey = !!storedCreds[svc];
            }
          } catch (e) {
            entry.status = storedCreds[svc] ? 'connected' : 'needs_key';
            entry.hasApiKey = !!storedCreds[svc];
          }
        }
      }

      // Check external services from services.json
      if (entry.status === 'not_found') {
        const externalService = storedServices.find(s => s.id === svc && s.isExternal);
        if (externalService?.externalUrl) {
          entry.source = 'external';
          entry.url = externalService.externalUrl;

          if (storedCreds[svc]) {
            entry.hasApiKey = true;
            entry.version = storedCreds[svc].metadata?.version || null;
            // Verify connection is still good
            const test = await helpers.testServiceConnection(svc, entry.url, storedCreds[svc].apiKey);
            entry.status = test.success ? 'connected' : 'error';
            if (test.success) entry.version = test.version;
          } else {
            entry.status = 'needs_key';
          }
        }
      }

      // Check stored credentials with metadata URL
      if (entry.status === 'not_found' && storedCreds[svc]?.metadata?.url) {
        entry.source = 'stored';
        entry.url = storedCreds[svc].metadata.url;
        entry.hasApiKey = true;
        entry.version = storedCreds[svc].metadata?.version || null;
        entry.status = 'connected';
      }

      // For plex, also check stored token
      if (svc === 'plex' && entry.status === 'not_found' && storedCreds.plex) {
        entry.hasToken = true;
        entry.source = 'stored';
        entry.url = storedCreds.plex.metadata?.url || `http://localhost:${defaultPorts.plex}`;
        entry.status = 'connected';
      }

      result[svc] = entry;
    }

    // 5. Detect seedbox base URL pattern
    let detectedSeedboxUrl = seedboxBaseUrl || null;
    if (!detectedSeedboxUrl) {
      const externalUrls = storedServices
        .filter(s => s.isExternal && s.externalUrl)
        .map(s => s.externalUrl);
      if (externalUrls.length > 0) {
        // Find common base URL pattern
        try {
          const url = new URL(externalUrls[0]);
          const pathParts = url.pathname.split('/').filter(p => p);
          if (pathParts.length >= 2) {
            detectedSeedboxUrl = `${url.origin}/${pathParts[0]}`;
          }
        } catch (e) { /* ignore */ }
      }
    }

    // Summary
    const statuses = Object.values(result);
    const summary = {
      totalDetected: statuses.filter(s => s.status !== 'not_found').length,
      fullyConnected: statuses.filter(s => s.status === 'connected').length,
      needsApiKey: statuses.filter(s => s.status === 'needs_key').length,
      errors: statuses.filter(s => s.status === 'error').length,
      readyForAutoConnect: statuses.filter(s => s.status === 'connected').length >= 2
    };

    ok(res, { services: result, seedboxBaseUrl: detectedSeedboxUrl, summary });
  }, 'smart-detect'));

  return router;
};
