const express = require('express');
const fs = require('fs');
const fsp = require('fs').promises;
const path = require('path');
const { exists, isAccessible } = require('../src/utilities/fs-helpers');
const { paginate, parsePaginationParams } = require('../src/utilities/pagination');
const { ValidationError, ForbiddenError } = require('../src/utilities/errors');
const { ok } = require('../src/utils/responses');

/**
 * Browse route factory
 * @param {Object} deps - Explicit dependencies
 * @param {Function} deps.asyncHandler - Async route handler wrapper
 * @param {Function} deps.validateSecurePath - Path traversal validator
 * @param {Object} deps.auditLogger - Audit logger
 * @param {Object} deps.docker - Docker client
 * @returns {express.Router}
 */
module.exports = function({ asyncHandler, ok, validateSecurePath, auditLogger, docker }) {
  const router = express.Router();

  // Parse browse roots from environment
  const BROWSE_ROOTS = (process.env.MEDIA_BROWSE_ROOTS || '')
    .split(',')
    .filter(r => r.includes('='))
    .map(r => {
      const eqIndex = r.indexOf('=');
      const containerPath = r.slice(0, eqIndex).trim();
      const hostPath = r.slice(eqIndex + 1).trim();
      return { containerPath, hostPath };
    });

  // Get available browse roots
  router.get('/browse/roots', asyncHandler(async (req, res) => {
    const allRoots = BROWSE_ROOTS.map(r => ({
      name: r.hostPath,
      path: r.hostPath,
      containerPath: r.containerPath
    }));

    const roots = [];
    for (const r of allRoots) {
      if (await isAccessible(r.containerPath, fs.constants.R_OK)) {
        roots.push(r);
      }
    }

    return ok(res, { roots });
  }, 'browse-roots'));

  // Browse directory contents
  router.get('/browse/directories', asyncHandler(async (req, res) => {
    const requestedPath = req.query.path || '';

    if (!requestedPath) {
      const allRoots = BROWSE_ROOTS.map(r => ({
        name: r.hostPath,
        path: r.hostPath,
        type: 'drive'
      }));
      const roots = [];
      for (const r of allRoots) {
        const br = BROWSE_ROOTS.find(br => br.hostPath === r.path);
        if (await isAccessible(br.containerPath, fs.constants.R_OK)) {
          roots.push(r);
        }
      }
      return ok(res, { path: '', items: roots });
    }

    const matchingRoot = BROWSE_ROOTS.find(r =>
      requestedPath.startsWith(r.hostPath) || requestedPath === r.hostPath.replace(/\/$/, '')
    );

    if (!matchingRoot) {
      throw new ValidationError('Path not in browseable roots', {
        availableRoots: BROWSE_ROOTS.map(r => r.hostPath)
      });
    }

    const relativePath = requestedPath.slice(matchingRoot.hostPath.length);
    const containerFullPath = path.join(matchingRoot.containerPath, relativePath);

    const allowedRoots = BROWSE_ROOTS.map(r => r.containerPath);
    let resolvedPath;
    try {
      resolvedPath = await validateSecurePath(containerFullPath, allowedRoots, auditLogger);
    } catch (error) {
      if (error.constructor.name === 'ValidationError') {
        auditLogger.logSecurityEvent('path_traversal_attempt', {
          requestedPath, containerFullPath, allowedRoots,
          error: error.message,
          ip: req.ip,
          userAgent: req.get('user-agent')
        });
        throw new ForbiddenError('Access denied - path traversal detected');
      }
      throw error;
    }

    if (!await exists(resolvedPath)) {
      const { NotFoundError } = require('../src/utilities/errors');
      throw new NotFoundError('Path');
    }

    const stats = await fsp.stat(resolvedPath);
    if (!stats.isDirectory()) {
      throw new ValidationError('Path is not a directory');
    }

    const entries = await fsp.readdir(resolvedPath, { withFileTypes: true });
    const folders = entries
      .filter(entry => {
        if (!entry.isDirectory()) return false;
        if (entry.name.startsWith('.')) return false;
        if (entry.name === '$RECYCLE.BIN' || entry.name === 'System Volume Information') return false;
        return true;
      })
      .map(entry => ({
        name: entry.name,
        path: path.join(requestedPath, entry.name).replace(/\\/g, '/'),
        type: 'folder'
      }))
      .sort((a, b) => a.name.localeCompare(b.name));

    const paginationParams = parsePaginationParams(req.query);
    const result = paginate(folders, paginationParams);
    ok(res, {
      path: requestedPath,
      parent: path.dirname(requestedPath).replace(/\\/g, '/') || null,
      items: result.data,
      ...(result.pagination && { pagination: result.pagination })
    });
  }, 'browse-dir'));

  // Detect media mounts from existing media server containers
  router.get('/media/detected-mounts', asyncHandler(async (req, res) => {
    const mediaServerPatterns = [
      'plex', 'jellyfin', 'emby', 'kodi', 'navidrome', 'airsonic',
      'subsonic', 'funkwhale', 'beets', 'lidarr', 'sonarr', 'radarr',
      'bazarr', 'readarr', 'prowlarr', 'overseerr', 'ombi', 'tautulli'
    ];

    const excludePatterns = [
      '/config', '/cache', '/transcode', '/data/config', '/app',
      '/tmp', '/var', '/etc', '/opt', '/root', '/home', '/.', '/caddyfile'
    ];

    const containers = await docker.client.listContainers({ all: false });
    const detectedMounts = [];
    const seenPaths = new Set();

    for (const containerInfo of containers) {
      const imageName = containerInfo.Image.toLowerCase();
      const isMediaServer = mediaServerPatterns.some(p => imageName.includes(p));
      if (!isMediaServer) continue;

      const container = docker.client.getContainer(containerInfo.Id);
      const details = await container.inspect();
      const binds = details.HostConfig?.Binds || [];

      for (const bind of binds) {
        const parts = bind.split(':');
        if (parts.length < 2) continue;

        let hostPath, containerPath;
        if (parts[0].length === 1 && /[A-Za-z]/.test(parts[0])) {
          hostPath = parts[0] + ':' + parts[1];
          containerPath = parts[2] || '';
        } else {
          hostPath = parts[0];
          containerPath = parts[1];
        }

        const isExcluded = excludePatterns.some(p =>
          containerPath.toLowerCase().includes(p.toLowerCase()) ||
          hostPath.toLowerCase().includes(p.toLowerCase())
        );
        if (isExcluded) continue;
        if (seenPaths.has(hostPath)) continue;
        seenPaths.add(hostPath);

        const folderName = hostPath.split(/[/\\]/).filter(p => p && p !== ':').pop() || hostPath;

        detectedMounts.push({
          hostPath, containerPath, folderName,
          sourceContainer: containerInfo.Names[0]?.replace('/', '') || containerInfo.Id.slice(0, 12),
          sourceImage: containerInfo.Image.split('/').pop().split(':')[0]
        });
      }
    }

    ok(res, {
      mounts: detectedMounts,
      message: detectedMounts.length > 0
        ? `Found ${detectedMounts.length} media mount(s) from existing containers`
        : 'No existing media mounts detected'
    });
  }, 'detect-media-mounts'));

  return router;
};
