const express = require('express');
const { APP_PORTS, ARR_SERVICES } = require('../../src/utilities/constants');
const { validateURL, validateToken } = require('../../src/security/input-validator');
const { ValidationError, AuthenticationError, NotFoundError } = require('../../src/utilities/errors');
const { logError } = require('../../src/utils/logging');
const { ok, successMessage } = require('../../src/utils/responses');

/**
 * Arr configuration routes factory
 * @param {Object} deps - Explicit dependencies
 * @param {Object} deps.credentialManager - Credential manager
 * @param {Object} deps.servicesStateManager - Services state manager
 * @param {Object} deps.docker - Docker client wrapper
 * @param {Function} deps.fetchT - Timeout-wrapped fetch
 * @param {Function} deps.asyncHandler - Async route handler wrapper
 * @param {Function} deps.errorResponse - Error response helper
 * @param {Object} deps.log - Logger instance
 * @param {Object} deps.helpers - Arr helpers module
 * @returns {express.Router}
 */
module.exports = function(ctx) {
  const { credentialManager, servicesStateManager, docker, fetchT, asyncHandler, errorResponse, log, helpers, notification, safeErrorMessage } = ctx;
  const router = express.Router();
  

  // Auto-configure Overseerr with detected services
  router.post('/arr/configure-overseerr', asyncHandler(async (req, res) => {
    const { radarr, sonarr } = req.body;
    const results = { radarr: null, sonarr: null };

    // Step 1: Authenticate with Overseerr via Plex token
    const overseerrUrl = `http://host.docker.internal:${APP_PORTS.overseerr}`;
    const overseerrSession = await helpers.getOverseerrSession();

    if (!overseerrSession) {
      return errorResponse(res, 502, 'Could not authenticate with Overseerr. Make sure Plex and Overseerr are running.', {
        hint: 'Complete Overseerr setup wizard and link your Plex account first, then try again.'
      });
    }

    log.info('arr', 'Authenticated with Overseerr via Plex session');

    // Helper to make authenticated requests to Overseerr
    const overseerrFetch = async (endpoint, options = {}) => {
      const url = `${overseerrUrl}${endpoint}`;
      const response = await fetchT(url, {
        ...options,
        headers: {
          'Content-Type': 'application/json',
          'Cookie': overseerrSession.cookie,
          ...options.headers
        }
      });
      return response;
    };

    // Step 2: Verify Overseerr is accessible
    try {
      const statusRes = await overseerrFetch('/api/v1/status');
      if (!statusRes.ok) {
        return errorResponse(res, 502, 'Cannot connect to Overseerr', {
          hint: 'Make sure Overseerr is running on port 5055'
        });
      }
    } catch (e) {
      return errorResponse(res, 502, `Cannot reach Overseerr: ${e.message}`, {
        hint: 'Check if Overseerr container is running'
      });
    }

    // Step 3: Configure Radarr if provided
    if (radarr?.apiKey && radarr?.url) {
      try {
        const radarrUrlObj = new URL(radarr.url);
        const radarrBasePath = radarrUrlObj.pathname.replace(/\/+$/, '');
        const radarrBaseUrl = radarr.url.replace(/\/+$/, '');

        // Fetch quality profiles from Radarr
        const profilesRes = await fetchT(`${radarrBaseUrl}/api/v3/qualityprofile`, {
          headers: { 'X-Api-Key': radarr.apiKey }
        });
        const profiles = profilesRes.ok ? await profilesRes.json() : [];
        const defaultProfile = profiles[0] || { id: 1, name: 'Any' };

        // Fetch root folders from Radarr
        const rootFoldersRes = await fetchT(`${radarrBaseUrl}/api/v3/rootfolder`, {
          headers: { 'X-Api-Key': radarr.apiKey }
        });
        const rootFolders = rootFoldersRes.ok ? await rootFoldersRes.json() : [];
        const defaultRootFolder = rootFolders[0]?.path || '/movies';

        log.info('arr', 'Radarr configured', { profile: defaultProfile.name, profileId: defaultProfile.id, rootFolder: defaultRootFolder });

        const radarrConfig = {
          name: 'Radarr',
          hostname: radarrUrlObj.hostname,
          port: parseInt(radarrUrlObj.port) || (radarrUrlObj.protocol === 'https:' ? 443 : APP_PORTS.radarr),
          apiKey: radarr.apiKey,
          useSsl: radarrUrlObj.protocol === 'https:',
          baseUrl: radarrBasePath || '',
          activeProfileId: defaultProfile.id,
          activeProfileName: defaultProfile.name,
          activeDirectory: defaultRootFolder,
          is4k: false,
          minimumAvailability: 'released',
          isDefault: true,
          externalUrl: radarr.url,
          tags: []
        };

        const radarrRes = await overseerrFetch('/api/v1/settings/radarr', {
          method: 'POST',
          body: JSON.stringify(radarrConfig)
        });

        if (radarrRes.ok) {
          results.radarr = 'configured';
        } else {
          const errorText = await radarrRes.text();
          results.radarr = `failed: ${errorText}`;
        }
      } catch (e) {
        results.radarr = `error: ${e.message}`;
      }
    }

    // Step 4: Configure Sonarr if provided
    if (sonarr?.apiKey && sonarr?.url) {
      try {
        const sonarrUrlObj = new URL(sonarr.url);
        const sonarrBasePath = sonarrUrlObj.pathname.replace(/\/+$/, '');
        const sonarrBaseUrl = sonarr.url.replace(/\/+$/, '');

        // Fetch quality profiles from Sonarr
        const profilesRes = await fetchT(`${sonarrBaseUrl}/api/v3/qualityprofile`, {
          headers: { 'X-Api-Key': sonarr.apiKey }
        });
        const profiles = profilesRes.ok ? await profilesRes.json() : [];
        const defaultProfile = profiles[0] || { id: 1, name: 'Any' };

        // Fetch root folders from Sonarr
        const rootFoldersRes = await fetchT(`${sonarrBaseUrl}/api/v3/rootfolder`, {
          headers: { 'X-Api-Key': sonarr.apiKey }
        });
        const rootFolders = rootFoldersRes.ok ? await rootFoldersRes.json() : [];
        const defaultRootFolder = rootFolders[0]?.path || '/tv';

        // Fetch language profiles from Sonarr (v3 uses languageprofile, v4 doesn't need it)
        let languageProfileId = 1;
        try {
          const langRes = await fetchT(`${sonarrBaseUrl}/api/v3/languageprofile`, {
            headers: { 'X-Api-Key': sonarr.apiKey }
          });
          if (langRes.ok) {
            const langProfiles = await langRes.json();
            languageProfileId = langProfiles[0]?.id || 1;
          }
        } catch (e) {
          // Language profiles might not exist in Sonarr v4
        }

        log.info('arr', 'Sonarr configured', { profile: defaultProfile.name, profileId: defaultProfile.id, rootFolder: defaultRootFolder });

        const sonarrConfig = {
          name: 'Sonarr',
          hostname: sonarrUrlObj.hostname,
          port: parseInt(sonarrUrlObj.port) || (sonarrUrlObj.protocol === 'https:' ? 443 : APP_PORTS.sonarr),
          apiKey: sonarr.apiKey,
          useSsl: sonarrUrlObj.protocol === 'https:',
          baseUrl: sonarrBasePath || '',
          activeProfileId: defaultProfile.id,
          activeProfileName: defaultProfile.name,
          activeDirectory: defaultRootFolder,
          activeLanguageProfileId: languageProfileId,
          is4k: false,
          isDefault: true,
          enableSeasonFolders: true,
          externalUrl: sonarr.url,
          tags: []
        };

        const sonarrRes = await overseerrFetch('/api/v1/settings/sonarr', {
          method: 'POST',
          body: JSON.stringify(sonarrConfig)
        });

        if (sonarrRes.ok) {
          results.sonarr = 'configured';
        } else {
          const errorText = await sonarrRes.text();
          results.sonarr = `failed: ${errorText}`;
        }
      } catch (e) {
        results.sonarr = `error: ${e.message}`;
      }
    }

    const anyConfigured = results.radarr === 'configured' || results.sonarr === 'configured';

    res.json({
      success: anyConfigured,
      message: anyConfigured ? 'Services configured in Overseerr' : 'Configuration failed',
      results
    });
  }, 'arr-configure-overseerr'));

  // Test connection to external Radarr/Sonarr service
  router.post('/arr/test-connection', asyncHandler(async (req, res) => {
    try {
      const { service, url, apiKey } = req.body;

      if (!url || !apiKey) {
        throw new ValidationError('URL and API key required');
      }

      // Validate URL format
      try {
        validateURL(url);
      } catch (validationErr) {
        return errorResponse(res, 400, validationErr.message);
      }

      // Validate API key format
      try {
        validateToken(apiKey);
      } catch (validationErr) {
        throw new ValidationError('Invalid API key format');
      }

      // Normalize URL - remove trailing slash
      const baseUrl = url.replace(/\/+$/, '');

      // Build the API endpoint
      let apiEndpoint;
      let headers = { 'X-Api-Key': apiKey, 'Accept': 'application/json' };

      if (service === 'radarr' || service === 'sonarr' || service === 'lidarr') {
        apiEndpoint = `${baseUrl}/api/v3/system/status`;
      } else if (service === 'prowlarr') {
        apiEndpoint = `${baseUrl}/api/v1/system/status`;
      } else if (service === 'plex') {
        apiEndpoint = `${baseUrl}/identity`;
        headers = { 'X-Plex-Token': apiKey, 'Accept': 'application/json' };
      } else {
        return errorResponse(res, 400, `Unknown service: ${service}`);
      }

      log.info('arr', 'Testing service connection', { service });

      // Make the API call
      const response = await fetchT(apiEndpoint, {
        method: 'GET',
        headers,
        signal: AbortSignal.timeout(10000)
      });

      if (response.ok) {
        const data = await response.json();
        const version = service === 'plex' ? data.MediaContainer?.version : data.version;
        const appName = service === 'plex' ? 'Plex' : data.appName;
        log.info('arr', 'Service connection successful', { service, appName, version });
        return ok(res, { version, appName });
      } else if (response.status === 401) {
        throw new AuthenticationError('Invalid API key');
      } else if (response.status === 404) {
        throw new NotFoundError('API not found - check URL');
      } else {
        return errorResponse(res, 502, `HTTP ${response.status}`);
      }
    } catch (error) {
      await logError('arr-test-connection', error);
      if (error.cause?.code === 'ECONNREFUSED') {
        return errorResponse(res, 502, 'Connection refused');
      } else if (error.name === 'AbortError' || error.message?.includes('timeout')) {
        return errorResponse(res, 504, 'Connection timeout');
      }
      return errorResponse(res, 500, safeErrorMessage(error));
    }
  }, 'arr-test-connection'));

  // Quick setup: Detect all services and configure Overseerr automatically
  router.post('/arr/auto-setup', asyncHandler(async (req, res) => {
    log.info('arr', 'Starting arr auto-setup');

    // Step 1: Detect all running arr services
    const containers = await docker.client.listContainers({ all: false });
    const detected = {};

    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))) {
          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(/^\//, ''),
            port: exposedPort,
            url: `http://host.docker.internal:${exposedPort}`,
            localUrl: `http://localhost:${exposedPort}`
          };

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

    // Step 2: Check what we found
    const summary = {
      overseerrFound: !!detected.overseerr,
      radarrFound: !!detected.radarr?.apiKey,
      sonarrFound: !!detected.sonarr?.apiKey,
      lidarrFound: !!detected.lidarr?.apiKey,
      prowlarrFound: !!detected.prowlarr?.apiKey
    };

    log.info('arr', 'Detected services', summary);

    if (!summary.overseerrFound) {
      return errorResponse(res, 400, 'Overseerr is not running. Deploy it first.', {
        detected,
        summary
      });
    }

    if (!summary.radarrFound && !summary.sonarrFound) {
      return errorResponse(res, 400, 'No Radarr or Sonarr found with valid API keys. Deploy at least one first.', {
        detected,
        summary
      });
    }

    // Step 3: Authenticate with Overseerr via Plex session
    const overseerrSession = await helpers.getOverseerrSession();

    if (!overseerrSession) {
      return errorResponse(res, 502, 'Could not authenticate with Overseerr. Make sure Plex and Overseerr are running.', {
        setupUrl: detected.overseerr.localUrl,
        detected,
        summary
      });
    }

    log.info('arr', 'Authenticated with Overseerr via Plex session');

    // Helper for authenticated Overseerr requests
    const overseerrFetch = async (endpoint, options = {}) => {
      return fetchT(`${detected.overseerr.url}${endpoint}`, {
        ...options,
        headers: {
          'Content-Type': 'application/json',
          'Cookie': overseerrSession.cookie,
          ...options.headers
        }
      });
    };

    // Step 4: Configure Radarr in Overseerr
    const configResults = {};

    if (detected.radarr?.apiKey) {
      try {
        // Fetch quality profiles from Radarr
        const profilesRes = await fetchT(`${detected.radarr.localUrl}/api/v3/qualityprofile`, {
          headers: { 'X-Api-Key': detected.radarr.apiKey }
        });
        const profiles = profilesRes.ok ? await profilesRes.json() : [];
        const defaultProfile = profiles[0] || { id: 1, name: 'Any' };

        // Fetch root folders from Radarr
        const rootFoldersRes = await fetchT(`${detected.radarr.localUrl}/api/v3/rootfolder`, {
          headers: { 'X-Api-Key': detected.radarr.apiKey }
        });
        const rootFolders = rootFoldersRes.ok ? await rootFoldersRes.json() : [];
        const defaultRootFolder = rootFolders[0]?.path || '/movies';

        log.info('arr', 'Radarr profile selected', { profile: defaultProfile.name, rootFolder: defaultRootFolder });

        const radarrConfig = {
          name: 'Radarr',
          hostname: 'host.docker.internal',
          port: detected.radarr.port,
          apiKey: detected.radarr.apiKey,
          useSsl: false,
          baseUrl: '',
          activeProfileId: defaultProfile.id,
          activeProfileName: defaultProfile.name,
          activeDirectory: defaultRootFolder,
          is4k: false,
          minimumAvailability: 'released',
          isDefault: true,
          externalUrl: detected.radarr.localUrl,
          tags: []
        };

        const resp = await overseerrFetch('/api/v1/settings/radarr', {
          method: 'POST',
          body: JSON.stringify(radarrConfig)
        });

        configResults.radarr = resp.ok ? 'configured' : `failed: ${await resp.text()}`;
      } catch (e) {
        configResults.radarr = `error: ${e.message}`;
      }
    }

    // Step 5: Configure Sonarr in Overseerr
    if (detected.sonarr?.apiKey) {
      try {
        // Fetch quality profiles from Sonarr
        const profilesRes = await fetchT(`${detected.sonarr.localUrl}/api/v3/qualityprofile`, {
          headers: { 'X-Api-Key': detected.sonarr.apiKey }
        });
        const profiles = profilesRes.ok ? await profilesRes.json() : [];
        const defaultProfile = profiles[0] || { id: 1, name: 'Any' };

        // Fetch root folders from Sonarr
        const rootFoldersRes = await fetchT(`${detected.sonarr.localUrl}/api/v3/rootfolder`, {
          headers: { 'X-Api-Key': detected.sonarr.apiKey }
        });
        const rootFolders = rootFoldersRes.ok ? await rootFoldersRes.json() : [];
        const defaultRootFolder = rootFolders[0]?.path || '/tv';

        // Fetch language profiles (Sonarr v3)
        let languageProfileId = 1;
        try {
          const langRes = await fetchT(`${detected.sonarr.localUrl}/api/v3/languageprofile`, {
            headers: { 'X-Api-Key': detected.sonarr.apiKey }
          });
          if (langRes.ok) {
            const langProfiles = await langRes.json();
            languageProfileId = langProfiles[0]?.id || 1;
          }
        } catch (e) { /* Sonarr v4 doesn't need this */ }

        log.info('arr', 'Sonarr profile selected', { profile: defaultProfile.name, rootFolder: defaultRootFolder });

        const sonarrConfig = {
          name: 'Sonarr',
          hostname: 'host.docker.internal',
          port: detected.sonarr.port,
          apiKey: detected.sonarr.apiKey,
          useSsl: false,
          baseUrl: '',
          activeProfileId: defaultProfile.id,
          activeProfileName: defaultProfile.name,
          activeDirectory: defaultRootFolder,
          activeLanguageProfileId: languageProfileId,
          is4k: false,
          isDefault: true,
          enableSeasonFolders: true,
          externalUrl: detected.sonarr.localUrl,
          tags: []
        };

        const resp = await overseerrFetch('/api/v1/settings/sonarr', {
          method: 'POST',
          body: JSON.stringify(sonarrConfig)
        });

        configResults.sonarr = resp.ok ? 'configured' : `failed: ${await resp.text()}`;
      } catch (e) {
        configResults.sonarr = `error: ${e.message}`;
      }
    }

    const anyConfigured = configResults.radarr === 'configured' || configResults.sonarr === 'configured';

    // Send notification
    if (anyConfigured) {
      notification.send(
        'deploymentSuccess',
        'Arr Stack Auto-Connected',
        `Overseerr configured: ${Object.entries(configResults).filter(([k,v]) => v === 'configured').map(([k]) => k).join(', ')}`,
        'success'
      );
    }

    res.json({
      success: anyConfigured,
      message: anyConfigured ? 'Auto-setup completed successfully!' : 'Configuration failed',
      detected,
      configResults,
      summary
    });
  }, 'arr-auto-setup'));

  // Fetch quality profiles from an arr service (Radarr/Sonarr)
  router.get('/arr/quality-profiles', asyncHandler(async (req, res) => {
    const { service, url, apiKey } = req.query;

    if (!service || !['radarr', 'sonarr'].includes(service)) {
      throw new ValidationError('Service must be radarr or sonarr');
    }

    // Resolve API key: from query param, or from stored credentials
    let resolvedKey = apiKey;
    let resolvedUrl = url;

    if (!resolvedKey) {
      resolvedKey = await credentialManager.retrieve(`arr.${service}.apikey`);
    }
    if (!resolvedKey) {
      resolvedKey = await credentialManager.retrieve(`service.${service}.apikey`);
    }

    if (!resolvedUrl) {
      const metadata = await credentialManager.getMetadata(`arr.${service}.apikey`);
      resolvedUrl = metadata?.url;
    }
    if (!resolvedUrl) {
      try {
        const services = await servicesStateManager.read();
        const svcList = Array.isArray(services) ? services : services.services || [];
        const found = svcList.find(s => s.id === service);
        if (found?.externalUrl) resolvedUrl = found.externalUrl;
        else if (found?.url) resolvedUrl = found.url;
      } catch (e) { /* ignore */ }
    }

    if (!resolvedKey || !resolvedUrl) {
      throw new ValidationError('Could not resolve API key or URL for this service');
    }

    const baseUrl = resolvedUrl.replace(/\/+$/, '');

    try {
      const profilesRes = await fetchT(`${baseUrl}/api/v3/qualityprofile`, {
        headers: { 'X-Api-Key': resolvedKey, 'Accept': 'application/json' },
        signal: AbortSignal.timeout(10000)
      });

      if (!profilesRes.ok) {
        return errorResponse(res, profilesRes.status === 401 ? 401 : 502,
          profilesRes.status === 401 ? 'Invalid API key' : `Failed to fetch profiles (HTTP ${profilesRes.status})`);
      }

      const profiles = await profilesRes.json();
      const mapped = profiles.map(p => ({ id: p.id, name: p.name }));

      // Load stored profile preference
      const metadata = await credentialManager.getMetadata(`arr.${service}.apikey`);
      const storedProfileId = metadata?.qualityProfileId || null;

      ok(res, { profiles: mapped, storedProfileId });
    } catch (e) {
      if (e.cause?.code === 'ECONNREFUSED') {
        return errorResponse(res, 502, 'Connection refused — is the service running?');
      }
      if (e.name === 'AbortError') {
        return errorResponse(res, 504, 'Connection timeout');
      }
      return errorResponse(res, 500, e.message);
    }
  }, 'arr-quality-profiles'));

  // Save quality profile preference (without re-storing API key)
  router.post('/arr/quality-profiles', asyncHandler(async (req, res) => {
    const { service, qualityProfileId, qualityProfileName } = req.body;

    if (!service || !['radarr', 'sonarr'].includes(service)) {
      throw new ValidationError('Service must be radarr or sonarr');
    }
    if (!qualityProfileId) {
      throw new ValidationError('qualityProfileId required');
    }

    const credKey = `arr.${service}.apikey`;
    const existing = await credentialManager.getMetadata(credKey);

    if (!existing) {
      throw new NotFoundError('No stored credentials for this service');
    }

    // Merge quality profile into existing metadata
    existing.qualityProfileId = qualityProfileId;
    existing.qualityProfileName = qualityProfileName || null;
    await credentialManager.storeMetadata(credKey, existing);

    successMessage(res, `Quality profile updated for ${service}`);
  }, 'arr-quality-profile-save'));

  return router;
};
