/**
 * Mengo Backend API
 * Express server with MongoDB and Redis
 */

import express from 'express';
import cors from 'cors';
import helmet from 'helmet';
import morgan from 'morgan';
import compression from 'compression';
import dotenv from 'dotenv';
import { createServer } from 'http';
import { WebSocketServer } from 'ws';
import fs from 'fs';
import path from 'path';

import { connectDatabase, isMockMode } from './utils/database';
import { connectRedis } from './utils/redis';
import { errorHandler } from './middleware/errorHandler';
import { rateLimiter } from './middleware/rateLimiter';

// Global unhandled rejection/exception handlers — prevent crashes from leaking as non-JSON 500s
process.on('unhandledRejection', (reason: any) => {
  console.error('[FATAL] Unhandled promise rejection:', reason);
});
process.on('uncaughtException', (err: Error) => {
  console.error('[FATAL] Uncaught exception:', err);
  // Do NOT exit — let the process continue so requests can still get error responses
});
import { checkSubscriptionLimit, ROUTE_LIMIT_MAP, isLimitedCreatePath } from './middleware/subscriptionCheck';
// NOTE: do NOT import './middleware/auth' at the top of this file. ES imports
// are hoisted above dotenv.config() below, and auth.ts resolves JWT_SECRET at
// module scope — importing it here made it fall back to the insecure dev secret
// while dualAuth.ts (loaded later, inside loadRoutes) used the real one. The
// mismatch made every dual-auth route (/auth/me, /module-data/*) reject valid
// session tokens. It is required lazily inside loadRoutes() instead.
import { requireModuleAccess, requireFeatureAccess, ROUTE_MODULE_MAP } from './middleware/packageAccess';
import { PRODUCTION_APP_URL } from './config/appUrls';

// Load environment variables — check multiple possible locations
const dns = require('dns');


dns.setServers(["8.8.8.8","8.8.4.4"]);
// EVERY candidate file is loaded, not just the first one that exists. dotenv
// never overwrites a variable that is already set, so precedence is unchanged —
// the first file still wins for anything it defines, and later files only fill
// in what earlier ones left out.
//
// Stopping at the first match is what made saved SMTP passwords "unsave"
// themselves: the repo-root .env and src/backend/.env point at the same
// database but the root one does not define ENCRYPTION_KEY. Starting the server
// from the repo root therefore loaded the root file, stopped, and left
// utils/encryption.ts on its insecure hardcoded fallback key — so a password
// encrypted by a run started in src/backend could not be decrypted by a run
// started at the root, and vice versa. The credential was always stored; it was
// simply written under one key and read back under another.
const envPaths = Array.from(new Set([
  path.resolve(process.cwd(), '.env'),
  path.resolve(__dirname, '../.env'),
  path.resolve(__dirname, '../../.env'),
]));
const loadedEnvFiles: string[] = [];
for (const envPath of envPaths) {
  if (!dotenv.config({ path: envPath }).error) loadedEnvFiles.push(envPath);
}
if (loadedEnvFiles.length > 0) {
  console.log(`📄 Loaded .env from: ${loadedEnvFiles.join(', ')}`);
} else {
  console.warn('⚠️  No .env file found — relying on the ambient process environment.');
}

const app = express();
const server = createServer(app);
const PORT = process.env.PORT || 3201;

// WebSocket server for real-time updates
const wss = new WebSocketServer({ server, path: '/ws' });

// Middleware
// Configure Helmet to allow cross-origin iframe embedding for landing page previews
// Build frame-ancestors origins with the same logic as CORS: production domain,
// configured frontend, ALLOWED_ORIGINS extras, and localhost in development only.
const frameAncestorOrigins = [
  "'self'",
  PRODUCTION_APP_URL,
  ...(process.env.ALLOWED_ORIGINS || '').split(',').filter(Boolean),
  ...(process.env.NODE_ENV !== 'production'
    ? ['http://localhost:3200', 'http://localhost:3000', 'http://localhost:5173', 'http://localhost:3001']
    : []),
];

app.use(helmet({
  contentSecurityPolicy: {
    directives: {
      defaultSrc: ["'self'"],
      // Allow the landing page preview iframe to be embedded by the frontend
      frameAncestors: frameAncestorOrigins,
    },
  },
  // Override X-Frame-Options — specific routes set their own headers
  frameguard: false,
}));
// Build CORS origins dynamically: production domain + configured frontend +
// any extra origins from ALLOWED_ORIGINS (comma-separated, for separate
// deployments) + localhost entries in development only.
const corsOrigins = [
  // The production app domain is always allowed — Stripe returns the buyer
  // there after checkout, and that page immediately calls the API to verify.
  PRODUCTION_APP_URL,
  process.env.FRONTEND_URL || PRODUCTION_APP_URL,
  // ALLOWED_ORIGINS: comma-separated list of additional origins (for separate
  // deployments where frontend and backend are on different domains).
  ...(process.env.ALLOWED_ORIGINS || '').split(',').filter(Boolean),
  // Localhost entries only in development — in production, use ALLOWED_ORIGINS
  ...(process.env.NODE_ENV !== 'production'
    ? ['http://localhost:3200', 'http://localhost:3000', 'http://localhost:5173', 'http://localhost:3001']
    : []),
];

app.use(cors({
  origin: corsOrigins,
  credentials: true
}));
app.use(compression());
app.use(morgan('dev'));
app.use(express.json({
  // Only the payment webhooks need the unparsed body (they verify an HMAC
  // signature over the exact bytes). Capturing it for EVERY request kept a
  // plaintext copy of every payload — including the passwords posted to
  // /auth/register, /auth/login and /auth/reset-password — hanging off the
  // request object for anything downstream to log or serialise.
  verify: (req: any, res, buf) => {
    if (typeof req.url === 'string' && req.url.includes('/webhook/')) {
      req.rawBody = buf.toString();
    }
  },
  limit: '50mb'
}));
app.use(express.urlencoded({ extended: true, limit: '50mb' }));

// Rate limiting
app.use(rateLimiter);

// Per-request context (who is acting), filled in by the authenticate middleware.
// Read by code that runs far from the route handler — AI job completion in
// particular — so a notification can be attributed to the right person.
app.use(require('./utils/requestContext').requestContextMiddleware);

// Ensure uploads directories exist
const uploadsDir = path.join(process.cwd(), 'uploads');
if (!fs.existsSync(uploadsDir)) {
  fs.mkdirSync(uploadsDir, { recursive: true });
  console.log('[Server] Created uploads directory:', uploadsDir);
}
const landingPagesDir = path.join(process.cwd(), 'uploads', 'landing-pages');
if (!fs.existsSync(landingPagesDir)) {
  fs.mkdirSync(landingPagesDir, { recursive: true });
  console.log('[Server] Created landing pages directory:', landingPagesDir);
}

// Static files for uploads — serve at both /uploads and /api/uploads
// so that relative paths like /uploads/<file> work with the API base URL
// Override Helmet's Cross-Origin-Resource-Policy so cross-origin frontends can load uploaded images
app.use('/uploads', (req, res, next) => {
  res.setHeader('Cross-Origin-Resource-Policy', 'cross-origin');
  next();
}, express.static('uploads'));
app.use('/api/uploads', (req, res, next) => {
  res.setHeader('Cross-Origin-Resource-Policy', 'cross-origin');
  next();
}, express.static('uploads'));

// Static files for landing page previews — served cross-origin for iframe preview
// Must set permissive CSP because generated landing pages use inline <style> for all CSS
// and inline <script> for interactivity. Helmet's default CSP blocks these.
app.use('/uploads/landing-pages', (req, res, next) => {
  res.setHeader('Cross-Origin-Resource-Policy', 'cross-origin');
  res.setHeader('X-Frame-Options', 'ALLOWALL');
  // Only set permissive CSP for HTML files (landing page previews)
  // Other file types (ZIP downloads, metadata.json) don't need it but it's harmless
  res.removeHeader('Content-Security-Policy');
  res.setHeader(
    'Content-Security-Policy',
    "default-src 'none'; " +
    "style-src 'unsafe-inline' 'self' https://fonts.googleapis.com; " +
    "font-src 'self' https://fonts.gstatic.com; " +
    "script-src 'unsafe-inline' 'self'; " +
    "img-src * data: blob:; " +
    "connect-src *; " +
    "frame-ancestors *; " +
    "base-uri 'none'; " +
    "form-action 'self'"
  );
  next();
}, express.static(path.join(process.cwd(), 'uploads', 'landing-pages')));

// Health check
app.get('/health', (req, res) => {
  res.json({
    status: 'ok',
    mode: isMockMode() ? 'mock' : 'mongodb',
    timestamp: new Date().toISOString()
  });
});

// Load routes after database connection
const loadRoutes = () => {
  // Import routes (models will be loaded dynamically based on mock mode)
  const authRoutes = require('./routes/auth').default;
  const twoFactorRoutes = require('./routes/twoFactor').default;
  const companyRoutes = require('./routes/companies').default;
  const userRoutes = require('./routes/users').default;
  const superAdminRoutes = require('./routes/superAdmin').default;
  const superAdminBackupLogsRoutes = require('./routes/superAdminBackupLogs').default;
  const securitySettingsRoutes = require('./routes/securitySettings').default;
  const superAdminBrandingRoutes = require('./routes/superAdminBranding').default;
  const privacyPolicySettingsRoutes = require('./routes/privacyPolicySettings').default;
  const termsSettingsRoutes = require('./routes/termsSettings').default;
  const aiKeyHealthRoutes = require('./routes/aiKeyHealth').default;
  const workflowTemplateAdminRoutes = require('./routes/workflowTemplates').default;
  const workflowTemplatePublicRoutes = require('./routes/workflowTemplatesPublic').default;
  const templateStyleAdminRoutes = require('./routes/templateStyles').default;
  const templateStylePublicRoutes = require('./routes/templateStylesPublic').default;
  const businessProfileRoutes = require('./routes/businessProfiles').default;
  const productRoutes = require('./routes/products').default;
  const founderRoutes = require('./routes/founders').default;
  const employeeRoutes = require('./routes/employees').default;
  const icpRoutes = require('./routes/icps').default;
  const personaRoutes = require('./routes/personas').default;
  const competitorRoutes = require('./routes/competitors').default;
  const brandRoutes = require('./routes/brands').default;
  const brandAssetRoutes = require('./routes/brandAssets').default;
  const imageGenerationRoutes = require('./routes/imageGenerations').default;
  const svgRenderRoutes = require('./routes/svgRender').default;
  const stationeryRoutes = require('./routes/stationery').default;
  const stationeryTemplateRoutes = require('./routes/stationeryTemplates').default;
  const brandKitRoutes = require('./routes/brandkits').default;
  const brandKitAssetRoutes = require('./routes/brandKitAssets').default;
  const brandKitTemplateRoutes = require('./routes/brandKitTemplates').default;
  const brandKitOrderRoutes = require('./routes/brandKitOrders').default;
  const websitePageRoutes = require('./routes/websitePages').default;
  const blogRoutes = require('./routes/blogs').default;
  const blogContentOsRoutes = require('./routes/blogContentOs').default;
  const newsletterContentOsRoutes = require('./routes/newsletterContentOs').default;
  const newsletterRoutes = require('./routes/newsletters').default;
  const landingPageContentOsRoutes = require('./routes/landingPageContentOs').default;
  const faqRoutes = require('./routes/faqs').default;
  const faqBankRoutes = require('./routes/faqBank').default;
  const testimonialRoutes = require('./routes/testimonials').default;
  const salesScriptRoutes = require('./routes/salesScripts').default;
  const salesCollateralRoutes = require('./routes/salesCollateral').default;
  const eventRoutes = require('./routes/events').default;
  const introScriptRoutes = require('./routes/introScripts').default;
  const speakingEngagementRoutes = require('./routes/speakingEngagements').default;
  const moatAnalysisRoutes = require('./routes/moatAnalysis').default;
  const swotAnalysisRoutes = require('./routes/swotAnalysis').default;
  const caseStudyRoutes = require('./routes/caseStudies').default;
  const moduleDataRoutes = require('./routes/moduleData').default;
  const foundationalContextRoutes = require('./routes/foundationalContext').default;
  const chatRoutes = require('./routes/chat').default;
  const taskRoutes = require('./routes/tasks').default;
  const aiRoutes = require('./routes/ai').default;
  const claudeRoutes = require('./routes/claude').default;
  const aiGeneratedVersionRoutes = require('./routes/aiGeneratedVersions').default;
  const adsRoutes = require('./routes/ads').default;
  const whatsappCampaignRoutes = require('./routes/whatsappCampaigns').default;
  const prRoutes = require('./routes/pr').default;
  const emailTemplateRoutes = require('./routes/emailTemplates').default;
  const uploadRoutes = require('./routes/upload').default;
  const referralRoutes = require('./routes/referrals').default;
  const bookRoutes = require('./routes/books').default;
  const aiContextRoutes = require('./routes/aiContext').default;
  const icpAiRoutes = require('./routes/aiContextIcp').default;
  const personaAiRoutes = require('./routes/aiContextPersona').default;
  const competitorAiRoutes = require('./routes/aiContextCompetitor').default;
  const brandAiRoutes = require('./routes/aiContextBrand').default;
  const brandAssetAiRoutes = require('./routes/aiContextBrandAsset').default;
  const brandAssetGuidelinesRoutes = require('./routes/brandAssetGuidelines').default;
  const websitePlannerAiRoutes = require('./routes/aiContextWebsitePlanner').default;
  const faqBankAiRoutes = require('./routes/aiContextFaqBank').default;
  const caseStudyAiRoutes = require('./routes/aiContextCaseStudy').default;
  const testimonialAiRoutes = require('./routes/aiContextTestimonial').default;
  const productAiRoutes = require('./routes/aiContextProduct').default;
  const blogAiRoutes = require('./routes/aiContextBlog').default;
  const newsletterAiRoutes = require('./routes/aiContextNewsletter').default;
  const landingPageAiRoutes = require('./routes/aiContextLandingPage').default;
  const salesScriptAiRoutes = require('./routes/aiContextSalesScript').default;
  const salesCollateralAiRoutes = require('./routes/aiContextSalesCollateral').default;
  const videoContentRoutes = require('./routes/videoContent').default;
  const audioContentRoutes = require('./routes/audioContents').default;
  const videoContentAiRoutes = require('./routes/aiContextVideoContent').default;
  const audioContentAiRoutes = require('./routes/aiContextAudioContent').default;
  const bookAiRoutes = require('./routes/aiContextBook').default;
  const adsAiRoutes = require('./routes/aiContextAds').default;
  const prAiRoutes = require('./routes/aiContextPR').default;
  const emailAiRoutes = require('./routes/aiContextEmail').default;
  const eventAiRoutes = require('./routes/aiContextEvent').default;
  const suggestionsAiRoutes = require('./routes/aiContextSuggestions').default;
  const introScriptAiRoutes = require('./routes/aiContextIntroScript').default;
  const speakingEngagementAiRoutes = require('./routes/aiContextSpeakingEngagement').default;
  const interviewMediaPrepAiRoutes = require('./routes/aiContextInterviewMediaPrep').default;
  const moatAnalysisAiRoutes = require('./routes/aiContextMoatAnalysis').default;
  const swotAnalysisAiRoutes = require('./routes/aiContextSwotAnalysis').default;
  const socialMediaAiRoutes = require('./routes/aiContextSocialMedia').default;
  const guerrillaMarketingAiRoutes = require('./routes/aiContextGuerrillaMarketing').default;
  const aiJobsRoutes = require('./routes/aiJobs').default;
  const aiGenerationsRoutes = require('./routes/aiGenerations').default;
  const aiImageRawRoutes = require('./routes/aiImageRaw').default;
  const marketingCalendarAiRoutes = require('./routes/aiContextMarketingCalendar').default;
  const marketingChannelsAiRoutes = require('./routes/aiContextMarketingChannels').default;
  const seoAiRoutes = require('./routes/aiContextSeo').default;
  // Subscription & Billing routes
  const subscriptionPackageRoutes = require('./routes/subscriptionPackages').default;
  const companySubscriptionRoutes = require('./routes/companySubscriptions').default;
  const couponRoutes = require('./routes/coupons').default;
  const paymentGatewayConfigRoutes = require('./routes/paymentGatewayConfig').default;
  const smtpConfigRoutes = require('./routes/smtpConfig').default;
  const currencyConfigRoutes = require('./routes/currencyConfig').default;
  const invoiceRoutes = require('./routes/invoices').default;
  const pricingConfigRoutes = require('./routes/pricingConfig').default;
  const subscriptionAuditLogRoutes = require('./routes/subscriptionAuditLogs').default;
  const geoCurrencyRoutes = require('./routes/geoCurrency').default;
  const youtubeAuthRoutes = require('./routes/youtubeAuth').default;
  const googleDriveAuthRoutes = require('./routes/googleDriveAuth').default;
  const n8nRoutes = require('./routes/n8n').default;
const zapierRoutes = require('./routes/zapier').default;
  const wordpressConfigRoutes = require('./routes/wordpressConfig').default;
  const wordpressRoutes = require('./routes/wordpress').default;
  const socialPublicationRoutes = require('./routes/socialPublications').default;
  const facebookAuthRoutes = require('./routes/facebookAuth').default;
  const facebookPublicationRoutes = require('./routes/facebookPublications').default;
  const facebookN8nAuthRoutes = require('./routes/facebookN8nAuth').default;
  const facebookN8nCallbackRoutes = require('./routes/facebookN8nCallback').default;
  const instagramAuthRoutes = require('./routes/instagramAuth').default;
  const instagramN8nAuthRoutes = require('./routes/instagramN8nAuth').default;
  const instagramN8nCallbackRoutes = require('./routes/instagramN8nCallback').default;
  const threadsN8nAuthRoutes = require('./routes/threadsN8nAuth').default;
  const threadsN8nCallbackRoutes = require('./routes/threadsN8nCallback').default;
  const instagramPublicationRoutes = require('./routes/instagramPublications').default;
  const linkedinAuthRoutes = require('./routes/linkedinAuth').default;
  const linkedinPublicationRoutes = require('./routes/linkedinPublications').default;
  const twitterAuthRoutes = require('./routes/twitterAuth').default;
  const twitterPublicationRoutes = require('./routes/twitterPublications').default;
  const threadsAuthRoutes = require('./routes/threadsAuth').default;
  const threadsPublicationRoutes = require('./routes/threadsPublications').default;
  const pinterestAuthRoutes = require('./routes/pinterestAuth').default;
  const pinterestPublicationRoutes = require('./routes/pinterestPublications').default;
  const googleAdsAuthRoutes = require('./routes/googleAdsAuth').default;
  const metaAdsAuthRoutes = require('./routes/metaAdsAuth').default;
  const whatsappAuthRoutes = require('./routes/whatsappAuth').default;
  const whatsappConnectionRoutes = require('./routes/whatsappConnection').default;
  const whatsappWebhookRoutes = require('./routes/whatsappWebhook').default;
  // Blogger (Google/Blogger integration)
  const bloggerAuthRoutes = require('./routes/bloggerAuth').default;
  const bloggerConnectionRoutes = require('./routes/bloggerConnection').default;
  const blogGenerationRoutes = require('./routes/blogGeneration').default;
  const socialMediaCampaignRoutes = require('./routes/socialMediaCampaigns').default;
  const guerrillaCampaignRoutes = require('./routes/guerrillaCampaigns').default;
  const socialContentOsRoutes = require('./routes/socialContentOS').default;
  const websiteGeneratorRoutes = require('./routes/websiteGenerator').default;
  const landingPageGeneratorRoutes = require('./routes/landingPageGenerator').default;
  const hostingConnectionRoutes = require('./routes/hostingConnections').default;
  const landingPageDeploymentRoutes = require('./routes/landingPageDeployments').default;
  const websiteDeploymentRoutes = require('./routes/websiteDeployments').default;
  const landingPageLeadRoutes = require('./routes/landingPageLeads').default;
  const bookGeneratorRoutes = require('./routes/bookGenerator').default;
  const interviewMediaPrepRoutes = require('./routes/interviewMediaPrep').default;
  const marketingCalendarRoutes = require('./routes/marketingCalendar').default;
  const geoOptimizationRoutes = require('./routes/geoOptimization').default;
  const marketingChannelsRoutes = require('./routes/marketingChannels').default;
  // Funding Module routes
  const pitchDeckRoutes = require('./routes/pitchDecks').default;
  const financialModelRoutes = require('./routes/financialModels').default;
  const investorRoutes = require('./routes/investors').default;
  const fundingRoundRoutes = require('./routes/fundingRounds').default;
  const capTableRoutes = require('./routes/capTable').default;
  const presentationsRoutes = require('./routes/presentations').default;
  const presentationGeneratorRoutes = require('./routes/presentationGenerator').default;
  // RBAC routes
  const roleRoutes = require('./routes/roles').default;
  const auditLogRoutes = require('./routes/auditLogs').default;
  const activityLogRoutes = require('./routes/activityLogs').default;
  const notificationRoutes = require('./routes/notifications').default;
  const userAccessOverrideRoutes = require('./routes/userAccessOverrides').default;
  const userManagementRoutes = require('./routes/userManagement').default;
  // Org-level admin routes
  const orgAdminRolesRoutes = require('./routes/orgAdminRoles').default;
  const orgAdminUsersRoutes = require('./routes/orgAdminUsers').default;
  const orgAdminAccessOverridesRoutes = require('./routes/orgAdminAccessOverrides').default;
  const orgAdminAuditLogsRoutes = require('./routes/orgAdminAuditLogs').default;
  const orgAdminEmailLogsRoutes = require('./routes/orgAdminEmailLogs').default;
  const orgAdminSecurityRoutes = require('./routes/orgAdminSecurity').default;
  // API Token Management & Documentation routes
  const apiTokenRoutes = require('./routes/apiTokens').default;
  const apiDocsRoutes = require('./routes/apiDocs').default;
  // Library — central icon repository (super-admin managed, all users read)
  const libraryRoutes = require('./routes/library').default;
  // Sales Performance routes
  const salesReportsRoutes = require('./routes/salesReports').default;
  const proposalsQuotesRoutes = require('./routes/proposalsQuotes').default;
  // Feature Request routes
  const featureRequestRoutes = require('./routes/featureRequests').default;
  const adminFeatureRequestRoutes = require('./routes/adminFeatureRequests').default;
  const supportTicketRoutes = require('./routes/supportTickets').default;
  const adminSupportTicketRoutes = require('./routes/adminSupportTickets').default;
  const supportTeamRoutes = require('./routes/supportTeams').default;
  const teamMemberRoutes = require('./routes/teamMembers').default;
  // Executive CV routes
  const executiveCVRoutes = require('./routes/executiveCV').default;
  // Wikipedia Profile routes
  const wikipediaProfileRoutes = require('./routes/wikipediaProfiles').default;
  // Email Signature routes
  const emailSignatureRoutes = require('./routes/emailSignatures').default;
  // Signature Template routes (Super Admin signature design library)
  const signatureTemplateRoutes = require('./routes/signatureTemplates').default;
  // Newsletter Template routes (Super Admin newsletter design library)
  const newsletterTemplateRoutes = require('./routes/newsletterTemplates').default;
  // Email Integration routes (Brevo)
  const emailIntegrationRoutes = require('./routes/emailIntegration').default;
  const emailWebhooksRoutes = require('./routes/emailWebhooks').default;
  // Automation Workflow routes
  const automationRoutes = require('./routes/automation').default;
  // Email Designer Template routes
  const emailDesignerTemplateRoutes = require('./routes/emailDesignerTemplates').default;
  // User Settings routes
  const userSettingsRoutes = require('./routes/userSettings').default;
  // Branding Settings routes
  const brandingSettingsRoutes = require('./routes/brandingSettings').default;
  // Backup & Restore routes
  const backupRestoreRoutes = require('./routes/backupRestore').default;

  // ── Subscription limit checks on create (POST) routes ──
  // MUST be registered BEFORE the route handlers below: Express runs middleware
  // never runs — the router responds first. (That was the original bug: package
  // limits were silently unenforced.)
  //
  // Guards, so nothing that worked before starts failing:
  //  - POST only; every other method passes straight through.
  //  - Only the router's root create (`/`) or a path listed in
  //    LIMIT_CREATE_SUBPATHS — sibling POSTs (categories, AI generation,
  //    uploads) are never blocked.
  //  - Only requests that carry an Authorization header; unauthenticated and
  //    public POSTs are left to the router's own auth handling.
  //  - `authenticate` runs first so req.user exists for the super-admin bypass
  //    and company resolution (it no-ops if a later handler already ran it).
  // Required here (not at the top of the file) so auth.ts is evaluated after
  // dotenv.config() has populated JWT_SECRET — see the note beside the imports.
  const { authenticate } = require('./middleware/auth');

  Object.entries(ROUTE_LIMIT_MAP).forEach(([routePath, resource]) => {
    app.use(routePath, (req: express.Request, res: express.Response, next: express.NextFunction) => {
      if (req.method !== 'POST' || !req.headers.authorization) {
        next();
        return;
      }
      if (!isLimitedCreatePath(routePath, req.path)) {
        next();
        return;
      }
      authenticate(req, res, () => checkSubscriptionLimit(resource)(req, res, next));
    });
  });

  // Global activity-capture: passively records every mutating API request via a
  // response 'finish' hook. Non-invasive (never reads/alters the body), and all
  // writes are fire-and-forget through activityLogger (which never throws), so
  // this cannot break or slow any existing feature. Mounted after auth-limit
  // wiring so req.user is populated by route auth before the log is written.
  const { activityCapture } = require('./middleware/activityCapture');
  app.use(activityCapture);

  // API Routes
  // Mounted before /api/auth so the 2FA sub-router owns /api/auth/2fa/* — the
  // auth router has no conflicting paths, but the explicit order keeps it that way.
  app.use('/api/auth/2fa', twoFactorRoutes);
  app.use('/api/auth', authRoutes);
  app.use('/api/upload', uploadRoutes);
  app.use('/api/companies', companyRoutes);
  app.use('/api/users', userRoutes);
  app.use('/api/user-settings', userSettingsRoutes);
  app.use('/api/branding-settings', brandingSettingsRoutes);
  // Mounted before the general super-admin router so these sub-paths resolve here.
  app.use('/api/super-admin/security', securitySettingsRoutes);
  app.use('/api/super-admin/backup-logs', superAdminBackupLogsRoutes);
  app.use('/api/super-admin/branding', superAdminBrandingRoutes);
  app.use('/api/super-admin/privacy-policy', privacyPolicySettingsRoutes);
  app.use('/api/super-admin/terms-of-service', termsSettingsRoutes);
  app.use('/api/super-admin', superAdminRoutes);
  app.use('/api/ai-key-health', aiKeyHealthRoutes);
  // Workflow template admin routes (already inside super-admin auth)
  app.use('/api/super-admin/workflow-templates', workflowTemplateAdminRoutes);
  // Workflow template public routes (authenticated users)
  app.use('/api', workflowTemplatePublicRoutes);
  // Template Style admin routes (super-admin CRUD) + public enabled list
  app.use('/api/super-admin/template-styles', templateStyleAdminRoutes);
  app.use('/api', templateStylePublicRoutes);
  // RBAC routes
  app.use('/api/roles', roleRoutes);
  app.use('/api/admin/users', userManagementRoutes);
  app.use('/api/admin/audit-logs', auditLogRoutes);
  app.use('/api/admin/activity-logs', activityLogRoutes);
  // Per-user notifications (scoped to the authenticated user inside the router)
  app.use('/api/notifications', notificationRoutes);
  app.use('/api/admin/access-overrides', userAccessOverrideRoutes);
  // Org-level admin routes
  app.use('/api/org-admin/roles', orgAdminRolesRoutes);
  app.use('/api/org-admin/users', orgAdminUsersRoutes);
  app.use('/api/org-admin/access-overrides', orgAdminAccessOverridesRoutes);
  app.use('/api/org-admin/audit-logs', orgAdminAuditLogsRoutes);
  app.use('/api/org-admin/email-logs', orgAdminEmailLogsRoutes);
  app.use('/api/org-admin/security', orgAdminSecurityRoutes);
  // API Token Management & Documentation
  app.use('/api/v1/admin/api-tokens', apiTokenRoutes);
  app.use('/api/v1/admin/api-docs', apiDocsRoutes);
  // Library — central icon repository
  app.use('/api/library', libraryRoutes);
  app.use('/api/business-profiles', businessProfileRoutes);
  app.use('/api/products', productRoutes);
  app.use('/api/founders', founderRoutes);
  app.use('/api/employees', employeeRoutes);
  app.use('/api/icps', icpRoutes);
  app.use('/api/personas', personaRoutes);
  app.use('/api/competitors', competitorRoutes);
  app.use('/api/brands', brandRoutes);
  app.use('/api/brand-assets', brandAssetRoutes);
  app.use('/api/image-generations', imageGenerationRoutes);
  app.use('/api/svg', svgRenderRoutes);
  app.use('/api/stationery', stationeryRoutes);
  app.use('/api/stationery-templates', stationeryTemplateRoutes);
  app.use('/api/brandkits', brandKitRoutes);
  app.use('/api/brand-kit-assets', brandKitAssetRoutes);
  app.use('/api/brand-kit-templates', brandKitTemplateRoutes);
  app.use('/api/brand-kit-orders', brandKitOrderRoutes);
  app.use('/api/website-pages', websitePageRoutes);
  app.use('/api/blogs', blogRoutes);
  app.use('/api/blog-content-os', blogContentOsRoutes);
  app.use('/api/blog-generation', blogGenerationRoutes);
  app.use('/api/social-media-campaigns', socialMediaCampaignRoutes);
  app.use('/api/guerrilla-campaigns', guerrillaCampaignRoutes);
  app.use('/api/social-content-os', socialContentOsRoutes);
  app.use('/api/newsletter-content-os', newsletterContentOsRoutes);
  app.use('/api/newsletters', newsletterRoutes);
  app.use('/api/landing-page-content-os', landingPageContentOsRoutes);
  app.use('/api/faqs', faqRoutes);
  app.use('/api/faq-bank', faqBankRoutes);
  app.use('/api/testimonials', testimonialRoutes);
  app.use('/api/sales-scripts', salesScriptRoutes);
  app.use('/api/sales-collateral', salesCollateralRoutes);
  app.use('/api/video-content', videoContentRoutes);
  app.use('/api/audio-contents', audioContentRoutes);
  app.use('/api/events', eventRoutes);
  app.use('/api/intro-scripts', introScriptRoutes);
  app.use('/api/speaking-engagements', speakingEngagementRoutes);
  app.use('/api/moat-analysis', moatAnalysisRoutes);
  app.use('/api/swot-analysis', swotAnalysisRoutes);
  app.use('/api/case-studies', caseStudyRoutes);
  app.use('/api/module-data', moduleDataRoutes);
  app.use('/api/foundational-context', foundationalContextRoutes);
  // Feature access checks — AI features require corresponding feature in subscription
  app.use('/api/chat', requireFeatureAccess('ai_chat'));
  app.use('/api/ai', requireFeatureAccess('ai_assistant'));
  app.use('/api/chat', chatRoutes);
  app.use('/api/tasks', taskRoutes);
  app.use('/api/ai', aiRoutes);
  app.use('/api/ai', aiImageRawRoutes);
  app.use('/api/claude', claudeRoutes);
  app.use('/api/ai-generated-versions', aiGeneratedVersionRoutes);
  app.use('/api/ads', adsRoutes);
  app.use('/api/whatsapp-campaigns', whatsappCampaignRoutes);
  app.use('/api/pr', prRoutes);
  app.use('/api/email-templates', emailTemplateRoutes);
  app.use('/api/books', bookRoutes);
  app.use('/api/ai-context', aiContextRoutes);
  app.use('/api/ai-context/icp', icpAiRoutes);
  app.use('/api/ai-context/persona', personaAiRoutes);
  app.use('/api/ai-context/competitor', competitorAiRoutes);
  app.use('/api/ai-context/brand', brandAiRoutes);
  app.use('/api/ai-context/brand-assets', brandAssetAiRoutes);
  app.use('/api/brand-asset-guidelines', brandAssetGuidelinesRoutes);
  app.use('/api/ai-context/website-planner', websitePlannerAiRoutes);
  app.use('/api/ai-context/faq-bank', faqBankAiRoutes);
  app.use('/api/ai-context/case-studies', caseStudyAiRoutes);
  app.use('/api/ai-context/testimonials', testimonialAiRoutes);
  app.use('/api/ai-context/product', productAiRoutes);
  app.use('/api/ai-context/blog', blogAiRoutes);
  app.use('/api/ai-context/newsletter', newsletterAiRoutes);
  app.use('/api/ai-context/landing-page', landingPageAiRoutes);
  app.use('/api/ai-context/sales-script', salesScriptAiRoutes);
  app.use('/api/ai-context/sales-collateral', salesCollateralAiRoutes);
  app.use('/api/ai-context/video-content', videoContentAiRoutes);
  app.use('/api/ai-context/audio-content', audioContentAiRoutes);
  app.use('/api/ai-context/book', bookAiRoutes);
  app.use('/api/ai-context/ads', adsAiRoutes);
  app.use('/api/ai-context/pr', prAiRoutes);
  app.use('/api/ai-context/email-templates', emailAiRoutes);
  app.use('/api/ai-context/event', eventAiRoutes);
  // Ranked "what should I do next" recommendations for the AI Chat generate picker.
  app.use('/api/ai-context/suggestions', suggestionsAiRoutes);
  app.use('/api/ai-context/intro-script', introScriptAiRoutes);
  app.use('/api/ai-context/speaking-engagement', speakingEngagementAiRoutes);
  app.use('/api/ai-context/interview-media-prep', interviewMediaPrepAiRoutes);
  app.use('/api/ai-context/moat-analysis', moatAnalysisAiRoutes);
  app.use('/api/ai-context/swot-analysis', swotAnalysisAiRoutes);
  app.use('/api/ai-context/social-media-os', socialMediaAiRoutes);
  app.use('/api/ai-context/guerrilla-marketing', guerrillaMarketingAiRoutes);
  app.use('/api/ai-context/marketing-calendar', marketingCalendarAiRoutes);
  app.use('/api/ai-context/marketing-channels', marketingChannelsAiRoutes);
  app.use('/api/ai-context/seo', seoAiRoutes);
  app.use('/api/ai-jobs', aiJobsRoutes);
  app.use('/api/ai-generations', aiGenerationsRoutes);
  app.use('/api/website-generator', websiteGeneratorRoutes);
  // Also mount website generator under ai-context for job polling compatibility
  app.use('/api/ai-context/website-generator', websiteGeneratorRoutes);
  app.use('/api/landing-page-generator', landingPageGeneratorRoutes);
  // Also mount landing page generator under ai-context for job polling compatibility
  app.use('/api/ai-context/landing-page-generator', landingPageGeneratorRoutes);
  // Landing page hosting/publishing (additive — hosting connections + deploy queue + public lead capture)
  app.use('/api/hosting/connections', hostingConnectionRoutes);
  app.use('/api/landing-page-deployments', landingPageDeploymentRoutes);
  // Website Planner hosting/publishing (additive — same deploy queue pattern as landing pages)
  app.use('/api/website-deployments', websiteDeploymentRoutes);
  app.use('/api/landing-page-leads', landingPageLeadRoutes);
  // Book content generator
  app.use('/api/book-generator', bookGeneratorRoutes);
  // Also mount book generator under ai-context for job polling compatibility
  app.use('/api/ai-context/book-generator', bookGeneratorRoutes);
  // Subscription & Billing
  app.use('/api/subscription-packages', subscriptionPackageRoutes);
  app.use('/api/company-subscriptions', companySubscriptionRoutes);
  app.use('/api/coupons', couponRoutes);
  app.use('/api/payment-gateway-config', paymentGatewayConfigRoutes);
  // Email Configuration — SMTP credentials every transactional email is sent through
  app.use('/api/smtp-config', smtpConfigRoutes);
  app.use('/api/currency-config', currencyConfigRoutes);
  app.use('/api/invoices', invoiceRoutes);
  app.use('/api/subscription-audit-logs', subscriptionAuditLogRoutes);
  app.use('/api/geo-currency', geoCurrencyRoutes);
  app.use('/api/pricing-config', pricingConfigRoutes);

  // YouTube connectivity routes (Social Media OS)
  app.use('/api/youtube/auth', youtubeAuthRoutes);
  // Google Drive integration routes (Backup & Restore)
  app.use('/api/google-drive/auth', googleDriveAuthRoutes);
  // n8n workflow automation routes
  app.use('/api/n8n', n8nRoutes);
  // Zapier automation routes
  app.use('/api/zapier', zapierRoutes);
  // WordPress config routes (Super Admin → Settings)
  app.use('/api/wordpress/config', wordpressConfigRoutes);
  // WordPress operational routes (posts, categories, tags, media, etc.)
  app.use('/api/wordpress', wordpressRoutes);
  // Facebook connectivity routes (Social Media OS)
  app.use('/api/facebook/auth', facebookAuthRoutes);
  // Facebook publication jobs (upload/publish/schedule/cancel)
  app.use('/api/facebook', facebookPublicationRoutes);
  // Facebook N8n connectivity routes (Social Media OS)
  app.use('/api/facebook-n8n', facebookN8nAuthRoutes);
  // Instagram N8n connectivity routes (Social Media OS — n8n-based publishing)
  app.use('/api/instagram-n8n', instagramN8nAuthRoutes);
  app.use('/api/threads-n8n', threadsN8nAuthRoutes);
  // Instagram connectivity routes (Social Media OS)
  app.use('/api/instagram/auth', instagramAuthRoutes);
  // Instagram publication jobs (upload/publish/schedule/cancel)
  app.use('/api/instagram', instagramPublicationRoutes);
  // LinkedIn connectivity routes (Social Media OS)
  app.use('/api/linkedin/auth', linkedinAuthRoutes);
  // LinkedIn publication jobs (upload/publish/schedule/cancel)
  app.use('/api/linkedin', linkedinPublicationRoutes);
  // X (Twitter) connectivity routes (Social Media OS)
  app.use('/api/twitter/auth', twitterAuthRoutes);
  // X (Twitter) publication jobs (upload/publish/schedule/cancel)
  app.use('/api/twitter', twitterPublicationRoutes);
  // Threads connectivity routes (Social Media OS)
  app.use('/api/threads/auth', threadsAuthRoutes);
  // Threads publication jobs (upload/publish/schedule/cancel)
  app.use('/api/threads', threadsPublicationRoutes);
  // Pinterest connectivity routes (Social Media OS)
  app.use('/api/pinterest/auth', pinterestAuthRoutes);
  // Pinterest board management + publication jobs (upload/publish/schedule/cancel)
  app.use('/api/pinterest', pinterestPublicationRoutes);
  // Google Ads API credentials (Super Admin → Settings)
  app.use('/api/ads/google/auth', googleAdsAuthRoutes);
  // Meta Ads API credentials (Super Admin → Settings)
  app.use('/api/ads/meta/auth', metaAdsAuthRoutes);
  // WhatsApp Business API credentials (Super Admin → Settings)
  app.use('/api/whatsapp/auth', whatsappAuthRoutes);
  // WhatsApp Business API per-tenant WABA connections (Admin → WhatsApp Nurturing)
  app.use('/api/whatsapp/connection', whatsappConnectionRoutes);
  // Blogger (Google/Blogger integration) — Super Admin config
  app.use('/api/blogger/auth', bloggerAuthRoutes);
  // Blogger (Google/Blogger integration) — Admin OAuth connections
  app.use('/api/blogger/connection', bloggerConnectionRoutes);
  // Blogger (Google/Blogger integration) — Publication jobs (CRUD + live API proxy)
  const bloggerPostsRoutes = require('./routes/bloggerPosts').default;
  app.use('/api/blogger/posts', bloggerPostsRoutes);
  // WhatsApp webhook (public — Meta sends events here, no auth)
  app.use('/webhooks', whatsappWebhookRoutes);
  // Facebook N8n callback (public — n8n sends events here, no auth)
  app.use('/webhooks', facebookN8nCallbackRoutes);
  // Instagram N8n callback (public — n8n sends events here, no auth)
  app.use('/webhooks', instagramN8nCallbackRoutes);
  app.use('/webhooks', threadsN8nCallbackRoutes);
  // Social media publication jobs (publish/schedule to connected platforms)
  app.use('/api/social-publications', socialPublicationRoutes);
  app.use('/api/interview-media-prep', interviewMediaPrepRoutes);
  app.use('/api/marketing-calendar', marketingCalendarRoutes);
  app.use('/api/marketing-channels', marketingChannelsRoutes);
  app.use('/api/geo-optimization', geoOptimizationRoutes);

  // Funding Module routes
  app.use('/api/pitch-decks', pitchDeckRoutes);
  app.use('/api/financial-models', financialModelRoutes);
  app.use('/api/investors', investorRoutes);
  app.use('/api/funding-rounds', fundingRoundRoutes);
  app.use('/api/cap-table', capTableRoutes);
  app.use('/api/presentations', presentationsRoutes);
  app.use('/api/presentation-generator', presentationGeneratorRoutes);
  app.use('/api/ai-context/presentation-generator', presentationGeneratorRoutes);

  // Sales Performance routes
  app.use('/api/sales-reports', salesReportsRoutes);
  app.use('/api/proposals-quotes', proposalsQuotesRoutes);

  // Feature Request routes
  app.use('/api/feature-requests', featureRequestRoutes);
  app.use('/api/admin/feature-requests', adminFeatureRequestRoutes);
  app.use('/api/support-tickets', supportTicketRoutes);
  app.use('/api/admin/support-tickets', adminSupportTicketRoutes);
  app.use('/api/support-teams', supportTeamRoutes);
  app.use('/api/team-members', teamMemberRoutes);
  // Executive CV routes
  app.use('/api/executive-cv', executiveCVRoutes);
  // Wikipedia Profile routes
  app.use('/api/wikipedia-profiles', wikipediaProfileRoutes);
  // Email Signature routes
  app.use('/api/email-signatures', emailSignatureRoutes);
  app.use('/api/signature-templates', signatureTemplateRoutes);
  app.use('/api/newsletter-templates', newsletterTemplateRoutes);
  app.use('/api/proposals-quotes', proposalsQuotesRoutes);

  // Email Integration routes (Brevo)
  app.use('/api/email-integration', emailIntegrationRoutes);
  // Webhook routes (public, no auth required)
  app.use('/webhooks', emailWebhooksRoutes);

  // Automation Workflow routes
  app.use('/api/automation', automationRoutes);

  // Email Designer Template routes
  app.use('/api/email-designer-templates', emailDesignerTemplateRoutes);

  // Backup & Restore routes
  app.use('/api/backup', backupRestoreRoutes);

  // Apply module access checks — ensures the user's subscription includes
  // the module before allowing access to its API routes.
  // Super-admins bypass all checks. Routes not in ROUTE_MODULE_MAP are not gated.
  Object.entries(ROUTE_MODULE_MAP).forEach(([routePrefix, moduleSlug]) => {
    app.use(routePrefix, requireModuleAccess(moduleSlug));
  });

  // Error handling
  app.use(errorHandler);

  // 404 handler
  app.use((req, res) => {
    res.status(404).json({ error: 'Not found' });
  });
};

// WebSocket connection handling
wss.on('connection', (ws, req) => {
  console.log('WebSocket client connected');

  ws.on('message', (message) => {
    try {
      const data = JSON.parse(message.toString());
      // Handle different message types
      if (data.type === 'subscribe' && data.companyId) {
        // Subscribe to company-specific updates
        (ws as any).companyId = data.companyId;
        ws.send(JSON.stringify({ type: 'subscribed', companyId: data.companyId }));
      }
    } catch (error) {
      ws.send(JSON.stringify({ type: 'error', message: 'Invalid message format' }));
    }
  });

  ws.on('close', () => {
    console.log('WebSocket client disconnected');
  });
});

// Broadcast function for real-time updates
export const broadcastToCompany = (companyId: string, data: any) => {
  wss.clients.forEach((client: any) => {
    if (client.companyId === companyId && client.readyState === 1) {
      client.send(JSON.stringify(data));
    }
  });
};

// Start server
const startServer = async () => {
  try {
    // Connect to MongoDB (or use mock)
    await connectDatabase();
    console.log(isMockMode() ? '📦 Using in-memory mock database' : '✅ MongoDB connected');

    // Connect to Redis (optional — won't block startup)
    await connectRedis();

    // Load routes after database connection
    loadRoutes();

    // Ensure Super Admin account exists
    try {
      const { getModels } = require('./models');
      const { User } = getModels();
      const superAdminEmail = process.env.SUPER_ADMIN_EMAIL || 'superadmin@mengo.com';
      const superAdminPassword = process.env.SUPER_ADMIN_PASSWORD;
      if (!superAdminPassword) {
        console.warn('⚠️ SUPER_ADMIN_PASSWORD not set. Super Admin account creation skipped for security. Set the environment variable to create the account on next startup.');
      } else {
        const existingSuperAdmin = await User.findOne({ role: 'super-admin' });
        if (!existingSuperAdmin) {
          await User.create({
            email: superAdminEmail,
            name: 'Super Admin',
            role: 'super-admin',
            passwordHash: superAdminPassword,
            companyIds: [],
          });
          console.log(`✅ Super Admin account created: ${superAdminEmail}`);
        } else {
          console.log('✅ Super Admin account already exists');
        }
      }
    } catch (err: any) {
      console.warn(`⚠️ Super Admin creation skipped: ${err.message}`);
    }

    // Seed prompt configurations from hardcoded defaults (upsert only, never overwrites)
    try {
      const { seedPromptConfigs } = await import('./utils/seedPromptConfigs');
      await seedPromptConfigs();
    } catch (err: any) {
      console.warn(`⚠️ Prompt config seeding skipped: ${err.message}`);
    }

    // Recover createdAt for interview sessions written while the subschema was
    // dropping it. Runs before the sweep below so stranded rows can be aged
    // against a restored updatedAt.
    try {
      const { backfillInterviewSessionCreatedAt } = await import('./utils/backfillInterviewSessionCreatedAt');
      const patched = await backfillInterviewSessionCreatedAt();
      if (patched > 0) {
        console.log(`✅ Backfilled createdAt for ${patched} interview session(s)`);
      }
    } catch (err: any) {
      console.warn(`⚠️ Interview session createdAt backfill skipped: ${err.message}`);
    }

    // Resolve Interview & Media Prep sessions stranded at 'generating' by a
    // previous process dying mid-generation (no catch block runs on a crash).
    try {
      const { sweepStuckInterviewSessions } = await import('./utils/sweepStuckInterviewSessions');
      const swept = await sweepStuckInterviewSessions();
      if (swept > 0) {
        // Sessions are embedded, so this counts company documents touched, not sessions.
        console.log(`✅ Reconciled stuck interview sessions in ${swept} company document(s)`);
      }
    } catch (err: any) {
      console.warn(`⚠️ Stuck interview session sweep skipped: ${err.message}`);
    }

    // Seed default workflow templates
    try {
      const { seedWorkflowTemplates } = await import('./utils/seedWorkflowTemplates');
      await seedWorkflowTemplates();
    } catch (err: any) {
      console.warn(`⚠️ Workflow template seeding skipped: ${err.message}`);
    }

    // Seed default template styles (famous marketing styles)
    try {
      const { seedTemplateStyles } = await import('./utils/seedTemplateStyles');
      await seedTemplateStyles();
    } catch (err: any) {
      console.warn(`⚠️ Template style seeding skipped: ${err.message}`);
    }

    // Seed default roles (upsert only, never overwrites)
    try {
      const { seedRoles } = await import('./scripts/seedRoles');
      await seedRoles();
    } catch (err: any) {
      console.warn(`⚠️ Role seeding skipped: ${err.message}`);
    }

    // Clear permission cache so fresh role permissions take effect
    try {
      const { invalidatePermissionCache } = await import('./middleware/permissions');
      // Clear all permission caches by pattern
      const { cacheClearPattern } = await import('./utils/redis');
      await cacheClearPattern('perms:*');
      console.log('[Startup] Permission cache cleared');
    } catch (err: any) {
      console.warn(`⚠️ Permission cache clear skipped: ${err.message}`);
    }

    // Migrate existing users: back-fill roleId and status
    try {
      const { migratePermissions } = await import('./scripts/migratePermissions');
      await migratePermissions();
    } catch (err: any) {
      console.warn(`⚠️ Permission migration skipped: ${err.message}`);
    }

    // Convert 2FA policies from role names to role ids. Runs after the role
    // seeding above so every name has something to resolve against.
    try {
      const { migrateTwoFactorRoles } = await import('./scripts/migrateTwoFactorRoles');
      await migrateTwoFactorRoles();
    } catch (err: any) {
      console.warn(`⚠️ 2FA role migration skipped: ${err.message}`);
    }

    // Reset the retired branding font-size default ('md') to 'xs' — see the
    // script for why only pre-cutoff documents are touched.
    try {
      const { migrateBrandingFontSize } = await import('./scripts/migrateBrandingFontSize');
      await migrateBrandingFontSize();
    } catch (err: any) {
      console.warn(`⚠️ Branding font size migration skipped: ${err.message}`);
    }

    // Seed pricing configuration (categories, items, rules, taxes, addons)
    try {
      const { seedPricingConfig } = await import('./scripts/seedPricingConfig');
      await seedPricingConfig();
    } catch (err: any) {
      console.warn(`⚠️ Pricing config seeding skipped: ${err.message}`);
    }

    // Migrate subscription packages with new pricing fields
    try {
      const { migrateSubscriptionPricing } = await import('./scripts/migrateSubscriptionPricing');
      await migrateSubscriptionPricing();
    } catch (err: any) {
      console.warn(`⚠️ Subscription pricing migration skipped: ${err.message}`);
    }

    // Migrate BrandKit assets/templates from legacy 'ai'/'manual' kind to 'template'
    try {
      const { getModels: getModelsBK } = require('./models');
      const { BrandKitAsset } = getModelsBK();
      const aiResult = await (BrandKitAsset as any).updateMany(
        { kind: { $in: ['ai', 'manual'] } },
        { $set: { kind: 'template' } }
      );
      if (aiResult.modifiedCount > 0) {
        console.log(`✅ Migrated ${aiResult.modifiedCount} BrandKitAsset(s) from 'ai'/'manual' → 'template'`);
      }
    } catch (err: any) {
      console.warn(`⚠️ BrandKitAsset kind migration skipped: ${err.message}`);
    }
    try {
      const { getModels: getModelsBK } = require('./models');
      const { BrandKitTemplate } = getModelsBK();
      const tmplResult = await (BrandKitTemplate as any).updateMany(
        { kind: { $in: ['ai', 'manual'] } },
        { $set: { kind: 'template' } }
      );
      if (tmplResult.modifiedCount > 0) {
        console.log(`✅ Migrated ${tmplResult.modifiedCount} BrandKitTemplate(s) from 'ai'/'manual' → 'template'`);
      }
      // Also migrate legacy 'ai-generation' source → 'predefined'
      const srcResult = await (BrandKitTemplate as any).updateMany(
        { source: 'ai-generation' },
        { $set: { source: 'predefined' } }
      );
      if (srcResult.modifiedCount > 0) {
        console.log(`✅ Migrated ${srcResult.modifiedCount} BrandKitTemplate(s) from 'ai-generation' → 'predefined' source`);
      }
    } catch (err: any) {
      console.warn(`⚠️ BrandKitTemplate kind migration skipped: ${err.message}`);
    }

    // Start HTTP server
    // Set generous timeouts for AI generation endpoints
    server.timeout = 900000;         // 15 minutes — accommodates long generations like Hindi speeches
    server.requestTimeout = 900000;  // 15 minutes
    server.keepAliveTimeout = 65000; // 65 seconds
    server.headersTimeout = 70000;   // 70 seconds (must be > keepAliveTimeout)

    server.listen(PORT, () => {
      console.log(`🚀 Server running on port ${PORT}`);
      console.log(`📡 WebSocket server ready on ws://localhost:${PORT}/ws`);
      console.log(`📝 API Documentation: http://localhost:${PORT}/health`);
    });

    // Durable publish queue for social media publications (YouTube uploads,
    // retries, and processing-status polling)
    const { startPublishWorker } = require('./services/youtube/publishWorker');
    startPublishWorker();

    // Start Automation Worker for workflow execution
    try {
      const { startAutomationWorker } = require('./workers/automationWorker');
      startAutomationWorker({
        pollInterval: 60000, // Poll every minute
        batchSize: 100,
        verbose: process.env.NODE_ENV === 'development',
      });
      console.log('✅ Automation Worker started');
    } catch (err: any) {
      console.warn(`⚠️ Automation Worker start skipped: ${err.message}`);
    }

    // Durable email dispatch queue — drains automation emails (async send +
    // retry/backoff/dead-letter) instead of sending synchronously in-node.
    try {
      const { startEmailDispatchWorker } = require('./services/email/emailDispatchWorker');
      startEmailDispatchWorker();
      console.log('✅ Email Dispatch Worker started');
    } catch (err: any) {
      console.warn(`⚠️ Email Dispatch Worker start skipped: ${err.message}`);
    }

    // AI Key Health Check Worker — periodically probes every configured AI provider
    // API key, marks failing keys Inactive and recovered keys Active, so generation
    // always selects a valid, active key without manual intervention.
    try {
      const { startAiKeyHealthWorker } = require('./workers/aiKeyHealthWorker');
      startAiKeyHealthWorker();
      console.log('✅ AI Key Health Check Worker started');
    } catch (err: any) {
      console.warn(`⚠️ AI Key Health Check Worker start skipped: ${err.message}`);
    }

    // Package Deletion Worker — hard-deletes subscription packages that were
    // queued for removal once their last live subscription has expired.
    try {
      const { startPackageDeletionWorker } = require('./workers/packageDeletionWorker');
      startPackageDeletionWorker();
      console.log('✅ Package Deletion Worker started');
    } catch (err: any) {
      console.warn(`⚠️ Package Deletion Worker start skipped: ${err.message}`);
    }

    // Hosting Deploy Worker — durable queue that publishes landing pages to hosting
    // providers (Netlify, Vercel, GitHub Pages, SFTP/FTP, S3) with retry/backoff.
    try {
      const { startHostingDeployWorker } = require('./services/hosting/hostingDeployWorker');
      startHostingDeployWorker();
      console.log('✅ Hosting Deploy Worker started');
    } catch (err: any) {
      console.warn(`⚠️ Hosting Deploy Worker start skipped: ${err.message}`);
    }

    // Website Deploy Worker — same durable queue, publishes Website Planner sites.
    try {
      const { startWebsiteDeployWorker } = require('./services/hosting/websiteDeployWorker');
      startWebsiteDeployWorker();
      console.log('✅ Website Deploy Worker started');
    } catch (err: any) {
      console.warn(`⚠️ Website Deploy Worker start skipped: ${err.message}`);
    }

    // Backup Worker — processes backup jobs from the queue, checks automatic
    // backup schedules, and applies auto-delete retention policies.
    try {
      const { startBackupWorker } = require('./workers/backupWorker');
      startBackupWorker();
      console.log('✅ Backup Worker started');
    } catch (err: any) {
      console.warn(`⚠️ Backup Worker start skipped: ${err.message}`);
    }

    // Subscription expiry sweep — warns admins at 7/3/1 days out and flips
    // lapsed subscriptions to 'expired'. Nothing else drives it: unlike payment
    // failures there is no webhook for "time passed", so it has to be polled.
    // Daily is the right cadence — the reminders are day-granular, and the
    // emitter's groupKey stops a restart from re-notifying the same day.
    try {
      const { checkExpiringSubscriptions } = require('./services/notificationService');
      const runExpirySweep = () => {
        checkExpiringSubscriptions().catch((err: any) =>
          console.error('[SubscriptionExpiry] Sweep failed:', err?.message),
        );
      };
      setInterval(runExpirySweep, 24 * 60 * 60 * 1000);
      // Delayed first run so a crash-loop restart cannot hammer it.
      setTimeout(runExpirySweep, 60 * 1000);
      console.log('✅ Subscription expiry sweep scheduled (daily)');
    } catch (err: any) {
      console.warn(`⚠️ Subscription expiry sweep start skipped: ${err.message}`);
    }
  } catch (error) {
    console.error('❌ Failed to start server:', error);
    process.exit(1);
  }
};

startServer();

// Graceful shutdown
process.on('SIGTERM', () => {
  console.log('SIGTERM received, shutting down gracefully');
  server.close(() => {
    console.log('Server closed');
    process.exit(0);
  });
});

process.on('SIGINT', () => {
  console.log('SIGINT received, shutting down gracefully');
  server.close(() => {
    console.log('Server closed');
    process.exit(0);
  });
});
