import express from 'express';
import helmet from 'helmet';
import morgan from 'morgan';
import dotenv from 'dotenv';
import cookieParser from 'cookie-parser';
import { UserService } from './services/user.service';
import { SSOUserData } from './types/auth.types';
import { sequelize } from './config/database';
import { ensureCpcCdcSchema } from './services/cpc-cdc/ensureCpcCdcSchema';
import { corsMiddleware } from './middlewares/cors.middleware';
import { authenticateToken } from './middlewares/auth.middleware';
import { requireAdmin } from './middlewares/authorization.middleware';
import { metricsMiddleware, createMetricsRouter } from './middlewares/metrics.middleware';
import routes from './routes/index';
import { registerCpcCsdCompatRoutes } from './routes/cpc-csd-compat.mount';
import form16Routes from './routes/form16.routes';
import { ensureUploadDir, UPLOAD_DIR } from './config/storage';
import { initializeGoogleSecretManager } from './services/googleSecretManager.service';
import { sanitizationMiddleware } from './middlewares/sanitization.middleware';
import { rateLimiter } from './middlewares/rateLimiter.middleware';
import path from 'path';

// Load environment variables from .env file first
dotenv.config();

// Secrets are now initialized in server.ts before app is imported

const app: express.Application = express();

// 1. Security middleware - Manual "Gold Standard" CSP to ensure it survives 301/404/etc.
// This handles a specific Express/Helmet edge case where redirects lose headers.
app.use((req: express.Request, res: express.Response, next: express.NextFunction) => {
  // Match server.ts: anything except production is "dev" for local tooling (.env often uses NODE_ENV=dev)
  const nodeEnv = (process.env.NODE_ENV || '').toLowerCase();
  const isDev = nodeEnv !== 'production' && nodeEnv !== 'prod';
  const frontendUrl = process.env.FRONTEND_URL || 'http://localhost:3000';

  // Build connect-src dynamically
  const connectSrc = ["'self'", "blob:", "data:"];
  if (isDev) {
    for (let port = 3000; port <= 3010; port++) {
      connectSrc.push(`http://localhost:${port}`, `http://127.0.0.1:${port}`);
      connectSrc.push(`ws://localhost:${port}`, `ws://127.0.0.1:${port}`);
    }
    for (let port = 5000; port <= 5005; port++) {
      connectSrc.push(`http://localhost:${port}`, `http://127.0.0.1:${port}`);
      connectSrc.push(`ws://localhost:${port}`, `ws://127.0.0.1:${port}`);
    }
    if (frontendUrl.includes('localhost') || frontendUrl.includes('127.0.0.1')) connectSrc.push(frontendUrl);
  } else if (frontendUrl && frontendUrl !== '*') {

    const origins = frontendUrl.split(',').map(url => url.trim()).filter(Boolean);
    connectSrc.push(...origins);
  }

  const apiDomain = process.env.APP_DOMAIN || 'royalenfield.com';

  // Define strict CSP directives
  //: Move frame-ancestors, form-action, and base-uri to the front to ensure VAPT compliance
  // even if the header is truncated in certain response types (like 301 redirects).
  const directives = [
    "frame-ancestors 'self'",
    "form-action 'self'",
    "base-uri 'self'",
    "default-src 'none'",
    `connect-src ${connectSrc.join(' ')}`,
    "style-src 'self' https://fonts.googleapis.com 'sha256-47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=' 'sha256-Od9mHMH7x2G6QuoV3hsPkDCwIyqbg2DX3F5nLeCYQBc=' 'sha256-eSB4TBEI8J+pgd6+gnmCP4Q+C+Yrx5BdjBEoPvZUzZI=' 'sha256-nzTgYzXYDNe6BAHiiI7NNlfK8n/auuOAhh2t92YvuXo=' 'sha256-441zG27rExd4/il+NvIqyL8zFx5XmyNQtE381kSkUJk='",
    "style-src-elem 'self' https://fonts.googleapis.com 'sha256-47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=' 'sha256-Od9mHMH7x2G6QuoV3hsPkDCwIyqbg2DX3F5nLeCYQBc=' 'sha256-eSB4TBEI8J+pgd6+gnmCP4Q+C+Yrx5BdjBEoPvZUzZI=' 'sha256-nzTgYzXYDNe6BAHiiI7NNlfK8n/auuOAhh2t92YvuXo=' 'sha256-441zG27rExd4/il+NvIqyL8zFx5XmyNQtE381kSkUJk='",
    "style-src-attr 'unsafe-inline'",
    "script-src 'self'",
    "script-src-elem 'self'",
    "script-src-attr 'none'",
    `img-src 'self' data: blob: https://*.${apiDomain} https://*.okta.com https://*.oktapreview.com https://*.googleapis.com https://*.gstatic.com`,
    "frame-src 'self' blob: data:",
    "font-src 'self' https://fonts.gstatic.com data:",
    "object-src 'none'",
    "worker-src 'self' blob:",
    "manifest-src 'self'",
    !isDev ? "upgrade-insecure-requests" : ""
  ].filter(Boolean).join("; ");

  res.setHeader('Content-Security-Policy', directives);
  next();
});

// Configure other security headers via Helmet (with CSP disabled since we set it manually)
app.use(helmet({
  contentSecurityPolicy: false, // Handled manually above to ensure redirect compatibility
  crossOriginEmbedderPolicy: false,
  crossOriginResourcePolicy: { policy: "cross-origin" },
  xFrameOptions: { action: "sameorigin" },
}));

// 2. CORS middleware - MUST be before other middleware
app.use(corsMiddleware);

// Handle /assets trailing slash redirect manually to avoid CSP truncation by express.static
app.get('/assets', (req, res) => {
  res.redirect(301, '/assets/');
});

// 3. Cookie parser middleware - MUST be before routes
app.use(cookieParser());

const userService = new UserService();

// Initializer for database connection (called from server.ts)
export const initializeAppDatabase = async () => {
  try {
    await sequelize.authenticate();
    console.log('✅ App database connection established');
    await ensureCpcCdcSchema();
  } catch (error) {
    console.error('❌ App database connection failed:', error);
    throw error;
  }
};

// Trust proxy - Enable this when behind a reverse proxy (nginx, load balancer, etc.)
// This allows Express to read X-Forwarded-* headers correctly
// Set to true in production, false in development
if (process.env.TRUST_PROXY === 'true' || process.env.NODE_ENV === 'production') {
  app.set('trust proxy', true);
} else {
  // In development, trust first proxy (useful for local testing with nginx)
  app.set('trust proxy', 1);
}

// Form 16 extract MUST be mounted BEFORE body parsers so multipart stream is not consumed
// (REform16 pattern: extract uses multer disk storage; mounting first guarantees raw stream for multer)
ensureUploadDir();
app.use('/api/v1/form16', form16Routes);

// Body parsing middleware
app.use(express.json({ limit: '10mb' }));
app.use(express.urlencoded({ extended: true, limit: '10mb' }));

// Global rate limiting disabled — nginx handles rate limiting in production
// app.use(rateLimiter);

// HTML sanitization - strip all tags from text inputs (after body parsing, before routes)
app.use(sanitizationMiddleware);

// Logging middleware
app.use(morgan('combined'));

// Prometheus metrics middleware - collect request metrics
app.use(metricsMiddleware);

// Prometheus metrics endpoint - expose metrics for scraping
app.use('/metrics', authenticateToken, requireAdmin, createMetricsRouter());

// Health check endpoint (before API routes)
app.get('/health', (_req: express.Request, res: express.Response) => {
  res.status(200).json({
    status: 'OK',
    timestamp: new Date(),
    uptime: process.uptime(),
    environment: process.env.NODE_ENV || 'development'
  });
});

// CPC-CSD-compatible paths (`/api/upload`, `/api/documents/*`, `/api/v1/ocr/*`) — same as `CPC-CSD/server` router
registerCpcCsdCompatRoutes(app);

// Mount API routes (form16 already mounted above before body parser)
app.use('/api/v1', routes);

// Serve uploaded files statically
ensureUploadDir();
app.use('/uploads', authenticateToken, express.static(UPLOAD_DIR));

// Initialize ClamAV toggle manager
import { initializeToggleFile } from './services/clamav/clamavToggleManager';
try {
  initializeToggleFile();
  console.log(`✅ ClamAV toggle initialized (ENABLE_CLAMAV=${process.env.ENABLE_CLAMAV || 'true'})`);
} catch (err) {
  console.warn('⚠️ ClamAV toggle initialization warning:', err);
}

// Legacy SSO Callback endpoint for user creation/update (kept for backward compatibility)
app.post('/api/v1/auth/sso-callback', async (req: express.Request, res: express.Response): Promise<void> => {
  try {
    const ssoData: SSOUserData = req.body;

    // Validate required fields - email and oktaSub are required
    if (!ssoData.email || !ssoData.oktaSub) {
      res.status(400).json({
        success: false,
        message: 'Missing required fields: email and oktaSub are required',
        timestamp: new Date()
      });
      return;
    }

    // Create or update user
    const user = await userService.createOrUpdateUser(ssoData);

    res.status(200).json({
      success: true,
      message: 'User processed successfully',
      data: {
        user: {
          userId: user.userId,
          employeeId: user.employeeId || null,
          oktaSub: user.oktaSub,
          email: user.email,
          firstName: user.firstName || null,
          lastName: user.lastName || null,
          displayName: user.displayName || null,
          department: user.department || null,
          designation: user.designation || null,
          phone: user.phone || null,
          location: user.location || null,
          role: user.role,
          lastLogin: user.lastLogin
        },
        isNewUser: user.createdAt.getTime() === user.updatedAt.getTime()
      },
      timestamp: new Date()
    });
  } catch (error) {
    console.error('❌ SSO Callback failed:', error);
    res.status(500).json({
      success: false,
      message: 'Internal server error',
      timestamp: new Date()
    });
  }
});

// Get all users endpoint
app.get('/api/v1/users', authenticateToken, requireAdmin, async (_req: express.Request, res: express.Response): Promise<void> => {
  try {
    const users = await userService.getAllUsers();

    res.status(200).json({
      success: true,
      message: 'Users retrieved successfully',
      data: {
        users: users.map(user => ({
          userId: user.userId,
          employeeId: user.employeeId || null,
          oktaSub: user.oktaSub,
          email: user.email,
          firstName: user.firstName || null,
          lastName: user.lastName || null,
          displayName: user.displayName || null,
          department: user.department || null,
          designation: user.designation || null,
          phone: user.phone || null,
          location: user.location || null,
          role: user.role,
          lastLogin: user.lastLogin,
          createdAt: user.createdAt
        })),
        total: users.length
      },
      timestamp: new Date()
    });
  } catch (error) {
    console.error('❌ Get Users failed:', error);
    res.status(500).json({
      success: false,
      message: 'Internal server error',
      timestamp: new Date()
    });
  }
});

// Serve React build static files (only in production or when build folder exists)
// Check for both 'build' (Create React App) and 'dist' (Vite) folders
const buildPath = path.join(__dirname, "..", "build");
const distPath = path.join(__dirname, "..", "dist");
const fs = require('fs');

// Try to find React build directory
let reactBuildPath: string | null = null;
if (fs.existsSync(buildPath)) {
  reactBuildPath = buildPath;
} else if (fs.existsSync(distPath)) {
  reactBuildPath = distPath;
}

// Serve static files if React build exists
if (reactBuildPath && fs.existsSync(path.join(reactBuildPath, "index.html"))) {
  // Serve static assets (JS, CSS, images, etc.) - these will have CSP headers from Helmet
  app.use(express.static(reactBuildPath, {
    setHeaders: (res: express.Response, filePath: string) => {
      // Apply CSP headers to HTML files served as static files
      if (filePath.endsWith('.html')) {
        // CSP headers are already set by Helmet middleware, but ensure they're applied
        // The meta tag in index.html will also enforce CSP
      }
    }
  }));

  // Catch-all handler: serve React app for all non-API routes
  // This must be AFTER all API routes to avoid intercepting API requests
  app.get('*', (req: express.Request, res: express.Response): void => {
    // Don't serve React for API routes, uploads, or health check
    if (req.path.startsWith('/api/') || req.path.startsWith('/uploads/') || req.path === '/health') {
      res.status(404).json({
        success: false,
        message: `Route ${req.originalUrl} not found`,
        timestamp: new Date(),
      });
      return;
    }

    // Serve React app for all other routes (SPA routing)
    // This handles client-side routing in React Router
    // CSP headers from Helmet will be applied to this response
    res.sendFile(path.join(reactBuildPath!, "index.html"));
  });
} else {
  // No React build found - provide API info at root and use standard 404 handler
  app.get('/', (_req: express.Request, res: express.Response): void => {
    res.status(200).json({
      message: 'Royal Enfield Workflow Management System API',
      version: '1.0.0',
      status: 'running',
      timestamp: new Date(),
      note: 'React build not found. API is available at /api/v1'
    });
  });

  // Standard 404 handler for non-existent routes
  app.use((req: express.Request, res: express.Response): void => {
    res.status(404).json({
      success: false,
      message: `Route ${req.originalUrl} not found`,
      timestamp: new Date(),
    });
  });
}

export default app;
