require('dotenv').config();
const express = require('express');
const path = require('path');
const cors = require('cors');
const helmet = require('helmet');
const compression = require('compression');
const morgan = require('morgan');
const session = require('express-session');
const SequelizeStore = require('connect-session-sequelize')(session.Store);
const { sequelize } = require('./models');
const {
    initializeTelegramPolling,
} = require('./modules/telegram/telegramInitializer');
const taskScheduler = require('./modules/tasks/taskScheduler');
const { setConfig, getConfig } = require('./config/config');
const config = getConfig();
const API_VERSION = process.env.API_VERSION || 'v1';
const API_BASE_PATH = `/api/${API_VERSION}`;

const app = express();

if (config.trustProxy !== false) {
    app.set('trust proxy', config.trustProxy);
    console.log(`[Trust Proxy] Enabled with value:`, config.trustProxy);
} else {
    console.log(`[Trust Proxy] Disabled (value: false)`);
}

console.log(`[Trust Proxy] Express setting confirmed:`, app.get('trust proxy'));

// Session store
const sessionStore = new SequelizeStore({
    db: sequelize,
});

// Middlewares
app.use(
    helmet({
        contentSecurityPolicy: {
            directives: {
                defaultSrc: ["'self'"],
                scriptSrc: ["'self'", "'unsafe-inline'", "'unsafe-eval'"],
                styleSrc: ["'self'", "'unsafe-inline'"],
                imgSrc: ["'self'", 'data:', 'https:'],
                connectSrc: ["'self'"],
                fontSrc: ["'self'"],
                objectSrc: ["'none'"],
                mediaSrc: ["'self'"],
                frameSrc: ["'none'"],
                upgradeInsecureRequests:
                    process.env.DISABLE_HSTS === 'true' ? null : [],
            },
        },
        hsts:
            config.production && process.env.DISABLE_HSTS !== 'true'
                ? {
                      maxAge: 31536000,
                      includeSubDomains: true,
                      preload: true,
                  }
                : false,
    })
);
app.use(compression());
app.use(morgan('combined'));

// CORS configuration
app.use(
    cors({
        origin: config.allowedOrigins,
        credentials: true,
        methods: [
            'GET',
            'POST',
            'PUT',
            'PATCH',
            'DELETE',
            'OPTIONS',
            'PROPFIND',
            'REPORT',
        ],
        allowedHeaders: [
            'Authorization',
            'Content-Type',
            'Accept',
            'X-Requested-With',
            'Depth',
            'If-Match',
            'If-None-Match',
        ],
        exposedHeaders: ['Content-Type', 'ETag', 'DAV', 'Allow'],
        maxAge: 1728000,
        preflightContinue: true,
    })
);

// Body parsing (skip for CalDAV routes which need raw body access)
app.use((req, res, next) => {
    const isCalDAVPath =
        req.path.startsWith('/caldav/') ||
        req.path.startsWith('/.well-known/caldav');

    if (isCalDAVPath) {
        return next();
    }

    express.json({ limit: '10mb' })(req, res, next);
});

app.use((req, res, next) => {
    const isCalDAVPath =
        req.path.startsWith('/caldav/') ||
        req.path.startsWith('/.well-known/caldav');

    if (isCalDAVPath) {
        return next();
    }

    express.urlencoded({ extended: true, limit: '10mb' })(req, res, next);
});

// CalDAV routes (registered after conditional body parsers)
const caldavRoutes = require('./modules/caldav/routes');
app.use(caldavRoutes);

// Session configuration
const sessionMiddleware = session({
    secret: config.secret,
    store: sessionStore,
    resave: false,
    saveUninitialized: false,
    cookie: {
        httpOnly: true,
        secure: process.env.COOKIE_SECURE !== 'false' && config.production,
        maxAge: 2592000000, // 30 days
        sameSite: 'lax',
    },
});
app.use(sessionMiddleware);

// CSRF protection using lusca (CodeQL recommended library)
const lusca = require('lusca');
const { csrfMiddleware } = require('./middleware/csrf');

// Pre-check middleware to exempt test/Bearer requests before lusca runs
app.use((req, res, next) => {
    // Public unauthenticated endpoints that should bypass CSRF
    // These are routes that don't require authentication and are used during login/registration
    const publicPaths = [
        '/api/login',
        '/api/register',
        '/api/verify-email',
        '/api/version',
        '/api/registration-status',
        '/api/health',
    ];

    const isPublicPath = publicPaths.some((path) => req.path === path);
    const isOidcPath = req.path.startsWith('/api/oidc/');
    const isFeatureFlagsPath = req.path.startsWith('/api/feature-flags');
    const isCalDAVPath =
        req.path.startsWith('/caldav/') ||
        req.path.startsWith('/.well-known/caldav');

    // Mark exempt requests so lusca wrapper can skip them
    if (
        process.env.NODE_ENV === 'test' ||
        req.headers.authorization?.startsWith('Bearer ') ||
        isPublicPath ||
        isOidcPath ||
        isFeatureFlagsPath ||
        isCalDAVPath
    ) {
        req._csrfExempt = true;
    }
    next();
});

// Apply lusca CSRF - wrapped to check exemption flag
// Only apply to state-changing methods (POST, PUT, PATCH, DELETE)
app.use((req, res, next) => {
    const statefulMethods = ['POST', 'PUT', 'PATCH', 'DELETE'];
    if (req._csrfExempt || !statefulMethods.includes(req.method)) {
        return next();
    }
    return csrfMiddleware(req, res, next);
});

// Static files
if (config.production) {
    app.use(express.static(path.join(__dirname, 'dist')));
} else {
    app.use(express.static('public'));
}

// Serve locales
if (config.production) {
    app.use('/locales', express.static(path.join(__dirname, 'dist/locales')));
} else {
    app.use(
        '/locales',
        express.static(path.join(__dirname, '../public/locales'))
    );
}

// Serve uploaded files
const registerUploadsStatic = (basePath) => {
    app.use(`${basePath}/uploads`, express.static(config.uploadPath));
};

registerUploadsStatic('/api');
if (API_VERSION && API_BASE_PATH !== '/api') {
    registerUploadsStatic(API_BASE_PATH);
}

// Authentication middleware
const { requireAuth } = require('./middleware/auth');
const { logError } = require('./services/logService');

// Rate limiting middleware
const {
    apiLimiter,
    authenticatedApiLimiter,
} = require('./middleware/rateLimiter');

// Error handler for modular architecture
const errorHandler = require('./shared/middleware/errorHandler');

// Modular routes
const adminModule = require('./modules/admin');
const areasModule = require('./modules/areas');
const authModule = require('./modules/auth');
const backupModule = require('./modules/backup');
const featureFlagsModule = require('./modules/feature-flags');
const habitsModule = require('./modules/habits');
const inboxModule = require('./modules/inbox');
const notesModule = require('./modules/notes');
const notificationsModule = require('./modules/notifications');
const projectsModule = require('./modules/projects');
const quotesModule = require('./modules/quotes');
const searchModule = require('./modules/search');
const sharesModule = require('./modules/shares');
const tagsModule = require('./modules/tags');
const tasksModule = require('./modules/tasks');
const telegramModule = require('./modules/telegram');
const urlModule = require('./modules/url');
const usersModule = require('./modules/users');
const viewsModule = require('./modules/views');
const mcpModule = require('./modules/mcp');
const oidcModule = require('./modules/oidc');

// Swagger documentation - enabled by default, protected by authentication
// Mounted on /api-docs to avoid conflicts with API routes
if (config.swagger.enabled) {
    const swaggerUi = require('swagger-ui-express');
    const swaggerSpec = require('./config/swagger');

    const swaggerUiOptions = {
        customSiteTitle: 'Tududi API Documentation',
        customfavIcon: '/favicon.ico',
        customCss: '.swagger-ui .topbar { display: none }',
        swaggerOptions: {
            url: '/api-docs/swagger.json',
        },
    };
    // Expose on /api-docs, protected by authentication
    app.use('/api-docs', requireAuth, swaggerUi.serve);
    app.get('/api-docs/swagger.json', requireAuth, (req, res) =>
        res.json(swaggerSpec)
    );
    app.get(
        '/api-docs',
        requireAuth,
        swaggerUi.serveFiles(swaggerSpec, swaggerUiOptions),
        swaggerUi.setup(swaggerSpec, swaggerUiOptions)
    );
}

// Apply rate limiting to API routes
// Use both limiters: apiLimiter for unauthenticated, authenticatedApiLimiter for authenticated
// Each has skip logic to handle their specific use case
const registerRateLimiting = (basePath) => {
    app.use(basePath, apiLimiter);
    app.use(basePath, authenticatedApiLimiter);
};

const rateLimitPath =
    API_VERSION && API_BASE_PATH !== '/api' ? API_BASE_PATH : '/api';
registerRateLimiting(rateLimitPath);

// Health check (before auth middleware) - ensure it's completely bypassed
const registerHealthCheck = (basePath) => {
    app.get(`${basePath}/health`, (req, res) => {
        res.status(200).json({
            status: 'ok',
            timestamp: new Date().toISOString(),
            uptime: process.uptime(),
            environment: config.environment,
            trustProxy: config.trustProxy,
        });
    });
};

const healthPaths = new Set(['/api']);
if (API_VERSION && API_BASE_PATH !== '/api') {
    healthPaths.add(API_BASE_PATH);
}
healthPaths.forEach(registerHealthCheck);

const registerApiRoutes = (basePath) => {
    app.use(basePath, authModule.routes);
    app.use(basePath, featureFlagsModule.routes);
    app.use(`${basePath}/oidc`, oidcModule.routes);

    app.use(basePath, requireAuth);
    app.use(basePath, tasksModule.routes);
    app.use(basePath, habitsModule.routes);
    app.use(basePath, projectsModule.routes);
    app.use(basePath, adminModule.routes);
    app.use(basePath, sharesModule.routes);
    app.use(basePath, areasModule.routes);
    app.use(basePath, notesModule.routes);
    app.use(basePath, tagsModule.routes);
    app.use(basePath, usersModule.routes);
    app.use(basePath, inboxModule.routes);
    app.use(basePath, urlModule.routes);
    app.use(basePath, telegramModule.routes);
    app.use(basePath, quotesModule.routes);
    app.use(basePath, backupModule.routes);
    app.use(basePath, searchModule.routes);
    app.use(basePath, viewsModule.routes);
    app.use(basePath, notificationsModule.routes);
    app.use(basePath, mcpModule.routes);
};

// Register routes at both /api and /api/v1 (if versioned) to maintain backwards compatibility
// The requireAuth middleware is applied once per base path, preventing the auth loop
const routeBases = new Set(['/api']);
if (API_VERSION && API_BASE_PATH !== '/api') {
    routeBases.add(API_BASE_PATH);
}
routeBases.forEach(registerApiRoutes);

// SPA fallback
app.get('*', (req, res) => {
    if (
        !req.path.startsWith('/api/') &&
        !req.path.match(/\.(js|css|png|jpg|jpeg|gif|ico|svg)$/)
    ) {
        if (config.production) {
            res.sendFile(path.join(__dirname, 'dist', 'index.html'));
        } else {
            res.sendFile(path.join(__dirname, '../public', 'index.html'));
        }
    } else {
        res.status(404).json({
            error: 'Not Found',
            message: 'The requested resource could not be found.',
        });
    }
});

// Error handling middleware (handles AppError and Sequelize errors)
app.use(errorHandler);

// Initialize database and start server
async function startServer() {
    try {
        // Create session store table
        await sessionStore.sync();

        // Initialize Telegram polling after database is ready
        await initializeTelegramPolling();

        // Initialize task scheduler
        await taskScheduler.initialize();

        // Initialize CalDAV sync scheduler
        const caldavSyncScheduler = require('./modules/caldav/services/sync-scheduler');
        await caldavSyncScheduler.initialize();

        // Validate authentication configuration
        const { validateAuthConfiguration } = require('./config/authConfig');
        validateAuthConfiguration();

        const server = app.listen(config.port, config.host, () => {
            console.log(`Server running on port ${config.port}`);
            console.log(`Server listening on http://localhost:${config.port}`);
        });

        server.on('error', (err) => {
            console.error('Server error:', err);
        });
    } catch (error) {
        console.error('Failed to start server:', error);
        process.exit(1);
    }
}

if (require.main === module) {
    startServer();
}

module.exports = app;
