const express = require('express');
const helmet = require('helmet');
const cors = require('cors');
const compression = require('compression');
const path = require('path');
const dotenv = require('dotenv');
const fileUpload = require('express-fileupload');
const crypto = require('crypto');
const logger = require('./utils/logger');
const { errorMiddleware, ErrorResponses } = require('./utils/errorHandler');
const db = require('./database/db');
const logManagement = require('./jobs/logManagement');

dotenv.config();

const app = express();
const PORT = process.env.PORT || 12345;
const isProduction = process.env.NODE_ENV === 'production';

// Generate nonce for inline scripts
app.use((req, res, next) => {
  res.locals.nonce = crypto.randomBytes(16).toString('base64');
  next();
});

// Security middleware with comprehensive CSP
app.use(helmet({
  contentSecurityPolicy: {
    directives: {
      defaultSrc: ["'self'"],
      scriptSrc: [
        "'self'",
        "'unsafe-inline'", // Required for React/Vite inline scripts
        "'unsafe-eval'",   // Required for React DevTools and some libraries
        "https://www.gstatic.com", // Google Cast SDK
        "https://cdn.jsdelivr.net", // HLS.js library
        "blob:",           // Required for HLS.js Web Workers
        (req, res) => `'nonce-${res.locals.nonce}'`
      ],
      workerSrc: [
        "'self'",
        "blob:"            // Required for HLS.js Web Workers
      ],
      styleSrc: [
        "'self'",
        "'unsafe-inline'", // Required for MUI and inline styles
        "https://fonts.googleapis.com"
      ],
      fontSrc: [
        "'self'",
        "data:",
        "https://fonts.gstatic.com"
      ],
      imgSrc: [
        "'self'",
        "data:",
        "blob:",
        "https:",
        "http:" // Allow external logo URLs
      ],
      mediaSrc: [
        "'self'",
        "blob:",
        "data:",
        "mediastream:",
        "https:",
        "http:", // Required for IPTV streams
        "*"      // Allow all media sources for streaming
      ],
      connectSrc: [
        "'self'",
        "https:",
        "http:",
        "ws:",
        "wss:", // WebSocket support
        "blob:",
        "*"     // Required for external APIs and streams
      ],
      frameSrc: [
        "'self'",
        "https://www.youtube.com", // For embedded players
        "https://player.vimeo.com"
      ],
      objectSrc: ["'none'"],
      baseUri: ["'self'"],
      formAction: ["'self'"],
      frameAncestors: ["'self'"]
      // upgradeInsecureRequests disabled for HTTP-only deployments
    },
    reportOnly: !isProduction, // Report-only mode in development
    useDefaults: false
  },
  originAgentCluster: false, // Disable to avoid agent cluster warnings
  crossOriginEmbedderPolicy: false,
  crossOriginOpenerPolicy: false,
  crossOriginResourcePolicy: { policy: "cross-origin" },
  hsts: isProduction ? {
    maxAge: 31536000,
    includeSubDomains: true,
    preload: true
  } : false,
  referrerPolicy: { policy: "strict-origin-when-cross-origin" },
  noSniff: true,
  xssFilter: true,
  hidePoweredBy: true
}));

// CORS configuration to allow local network and HTTPS domain
const allowedOrigins = [
  'http://localhost:12345',
  'http://localhost:9000',
  'https://tv.iulian.uk',
  'http://tv.iulian.uk',
  /^http:\/\/192\.168\.\d{1,3}\.\d{1,3}(:\d+)?$/, // Local network 192.168.x.x
  /^http:\/\/10\.\d{1,3}\.\d{1,3}\.\d{1,3}(:\d+)?$/, // Local network 10.x.x.x
  /^http:\/\/172\.(1[6-9]|2[0-9]|3[0-1])\.\d{1,3}\.\d{1,3}(:\d+)?$/ // Local network 172.16-31.x.x
];

// Mount logo-proxy BEFORE global CORS to handle public image serving
app.use('/api/logo-proxy', require('./routes/logo-proxy'));

app.use(cors({
  origin: function (origin, callback) {
    // Allow requests with no origin (mobile apps, curl, etc.)
    if (!origin) return callback(null, true);
    
    // Check if origin matches allowed patterns
    const isAllowed = allowedOrigins.some(allowed => {
      if (typeof allowed === 'string') {
        return origin === allowed;
      } else if (allowed instanceof RegExp) {
        return allowed.test(origin);
      }
      return false;
    });
    
    if (isAllowed) {
      callback(null, true);
    } else {
      console.warn(`[CORS] Rejected origin: ${origin}`);
      callback(new Error('Not allowed by CORS'));
    }
  },
  credentials: true, // Allow cookies and authentication headers
  methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS'],
  allowedHeaders: ['Content-Type', 'Authorization', 'X-Requested-With']
}));

app.use(compression());
app.use(express.json({ limit: '10mb' }));
app.use(express.urlencoded({ extended: true, limit: '10mb' }));
app.use(fileUpload({
  limits: { fileSize: 100 * 1024 * 1024 }, // 100MB max file size
  useTempFiles: true,
  tempFileDir: '/tmp/'
}));

// Initialize database
db.initialize();

// Serve static files (uploaded logos)
app.use('/uploads', express.static(path.join(__dirname, 'uploads')));

// Serve cached logos from IPTV-org
app.use('/logos', express.static(path.join('/app', 'data', 'logo-cache')));

// Health check endpoint (no rate limiting)
app.get('/api/health', (req, res) => {
  res.status(200).json({ status: 'ok', timestamp: new Date().toISOString() });
});

// API Routes
app.use('/api/auth', require('./routes/auth'));
app.use('/api/users', require('./routes/users'));
app.use('/api/playlists', require('./routes/playlists'));
app.use('/api/channels', require('./routes/channels'));
app.use('/api/recordings', require('./routes/recordings'));
app.use('/api/two-factor', require('./routes/twoFactor'));
app.use('/api/profiles', require('./routes/profiles'));
app.use('/api/radio', require('./routes/radio'));
app.use('/api/groups', require('./routes/groups'));
app.use('/api/settings', require('./routes/settings'));
app.use('/api/stream', require('./routes/stream'));
app.use('/api/stats', require('./routes/stats'));
app.use('/api/m3u-files', require('./routes/m3u-files'));
app.use('/api/vpn-configs', require('./routes/vpn-configs'));
app.use('/api/search', require('./routes/search'));
app.use('/api/favorites', require('./routes/favorites'));
app.use('/api/backup', require('./routes/backup'));
app.use('/api/metadata', require('./routes/metadata'));
app.use('/api/history', require('./routes/history'));
app.use('/api/logo-cache', require('./routes/logo-cache'));
app.use('/api/sessions', require('./routes/sessions'));
app.use('/api/csp', require('./routes/csp'));
app.use('/api/rbac', require('./routes/rbac'));
app.use('/api/security-monitor', require('./routes/security-monitor'));
app.use('/api/security-headers', require('./routes/security-headers'));
app.use('/api/security-testing', require('./routes/security-testing'));
app.use('/api/security-config', require('./routes/security-config'));
app.use('/api/siem', require('./routes/siem'));
app.use('/api/log-management', require('./routes/log-management'));
app.use('/api/encryption', require('./routes/encryption-management'));

// Serve static files from frontend
app.use(express.static(path.join(__dirname, '../frontend/dist')));

// Serve uploaded files
app.use('/uploads', express.static(path.join(__dirname, '../data/uploads')));
app.use('/logos', express.static(path.join(__dirname, '../data/logos')));
app.use('/data/logo-cache', express.static(path.join(__dirname, '../data/logo-cache')));

// Handle SPA routing (must be before error handler)
app.get('*', (req, res) => {
  res.sendFile(path.join(__dirname, '../frontend/dist/index.html'));
});

// Secure error handling middleware (CWE-209 protection)
// This MUST be the last middleware - catches all errors and sanitizes them
app.use(errorMiddleware);

// Reset VPN states on startup (connections are lost on restart)
db.db.run('UPDATE vpn_configs SET is_active = 0', (err) => {
  if (err) {
    logger.error('Failed to reset VPN states on startup:', err);
  } else {
    logger.info('Reset all VPN connection states on startup');
  }
});

// Start background jobs
require('./jobs/channelHealth');
require('./jobs/recordingScheduler');
require('./jobs/logoCacher');

// Process-level error handlers (CWE-391 protection)
// Handle uncaught exceptions
process.on('uncaughtException', (error) => {
  logger.error('UNCAUGHT EXCEPTION - Process will exit', {
    error: error.message,
    stack: error.stack,
    timestamp: new Date().toISOString()
  });
  // Give logger time to write
  setTimeout(() => {
    process.exit(1);
  }, 1000);
});

// Handle unhandled promise rejections
process.on('unhandledRejection', (reason, promise) => {
  logger.error('UNHANDLED PROMISE REJECTION', {
    reason: reason instanceof Error ? reason.message : reason,
    stack: reason instanceof Error ? reason.stack : undefined,
    promise: promise.toString(),
    timestamp: new Date().toISOString()
  });
});

// Handle SIGTERM gracefully
process.on('SIGTERM', () => {
  logger.info('SIGTERM received, closing server gracefully');
  // Close database connections, clear intervals, etc.
  db.db.close((err) => {
    if (err) {
      logger.error('Error closing database:', err);
    }
    process.exit(0);
  });
});

// Handle SIGINT (Ctrl+C) gracefully
process.on('SIGINT', () => {
  logger.info('SIGINT received, closing server gracefully');
  process.exit(0);
});

// Start server
const server = app.listen(PORT, '0.0.0.0', () => {
  logger.info(`StreamFlow server running on port ${PORT}`);
  console.log(`StreamFlow server running on port ${PORT}`);
  
  // Initialize log management system (CWE-53 compliance)
  logManagement.initialize().catch(err => {
    logger.error('Failed to initialize log management:', err);
  });
});

// Handle server errors
server.on('error', (error) => {
  logger.error('Server error:', {
    error: error.message,
    code: error.code,
    stack: error.stack
  });
  if (error.code === 'EADDRINUSE') {
    logger.error(`Port ${PORT} is already in use`);
    process.exit(1);
  }
});

module.exports = app;
