import { Server } from '@overnightjs/core';
import cors from 'cors';
import 'dotenv/config';
import express, { NextFunction, Request, Response } from 'express';
import mongoose from 'mongoose';
import path from 'path';
import 'reflect-metadata';
import { CONTROLLERS } from './controllers';
import { initFailedPaymentCron } from './cron/paymentCron';
import { initSubscriptionCron } from './cron/subscriptionCron';
import Plan from './models/Plan';

// Global connection cache for serverless environments
let cachedConnection: typeof mongoose | null = null;

class AppServer extends Server {
  private port: number;
  private mongoUri: string;

  constructor() {
    super();
    this.port = parseInt(process.env.PORT!, 10);
    this.mongoUri = process.env.MONGODB_URI!;
    this.setup();
  }

  private setup(): void {
    // Middleware - CORS must be first
    this.app.use(cors({
      origin: '*',
      credentials: false,
      methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS', 'PATCH'],
      allowedHeaders: ['Content-Type', 'Authorization', 'x-auth-token']
    }));
    this.app.use(express.json({
      limit: '15mb',
      verify: (req: any, res, buf) => {
        if (req.originalUrl.includes('/api/payments/webhook')) {
          req.rawBody = buf.toString();
        }
      }
    }));
    this.app.use(express.urlencoded({ extended: true, limit: '15mb' }));

    // Serve uploaded files from local disk
    this.app.use('/uploads', express.static(path.join(process.cwd(), 'public', 'uploads')));
    this.app.use('/api/uploads', express.static(path.join(process.cwd(), 'public', 'uploads')));

    // Ensure DB connection for each request in serverless environments
    this.app.use(async (req: Request, res: Response, next: NextFunction) => {
      try {
        if (!cachedConnection) {
          await this.connectDB();
        }
        next();
      } catch (err) {
        console.error('Database connection error in middleware:', err);
        res.status(503).json({ success: false, error: 'Database connection failed' });
      }
    });

    // Register all controllers
    this.addControllers(CONTROLLERS);

    // Health check route
    this.app.get('/api/health', (req: Request, res: Response) => {
      res.json({ status: 'OK', message: 'Server is running' });
    });

    // 404 handler (must be last)
    this.app.use((req: Request, res: Response) => {
      res.status(404).json({ success: false, error: 'Route not found' });
    });

    // Error handling middleware
    this.app.use((err: any, req: Request, res: Response, next: NextFunction) => {
      if (err?.type === 'entity.too.large' || err?.status === 413) {
        return res.status(413).json({
          success: false,
          error: 'Uploaded payload is too large. Please use a smaller image file.'
        });
      }
      console.error(err.stack);
      res.status(500).json({ success: false, error: 'Internal Server Error' });
    });
  }

  private async connectDB(): Promise<void> {
    try {
      // For serverless environments, reuse cached connection
      if (cachedConnection) {
        console.log('✓ Using cached MongoDB connection');
        return;
      }

      const connection = await mongoose.connect(this.mongoUri, {
        maxPoolSize: 10,
        minPoolSize: 2,
        socketTimeoutMS: 45000,
        connectTimeoutMS: 15000,
        serverSelectionTimeoutMS: 15000,
        retryWrites: true,
        w: 'majority',
      });

      cachedConnection = connection;
      console.log('✓ MongoDB connected successfully');
    } catch (err) {
      console.error('MongoDB connection error:', err);
      if (process.env.NODE_ENV !== 'production') {
        process.exit(1);
      }
      throw err;
    }
  }

  public async start(): Promise<void> {
    await this.connectDB();
    await this.seedFreeForeverPlan();
    this.app.listen(this.port, () => {
      console.log(`✓ Server is running on port ${this.port}`);
      console.log(`✓ API endpoints available at http://localhost:${this.port}/api`);
      initSubscriptionCron();
      initFailedPaymentCron();
    });
  }

  private async seedFreeForeverPlan(): Promise<void> {
    try {
      const exists = await Plan.findOne({ title: 'Free Forever', price: 0, period: 'freely' });
      if (!exists) {
        await Plan.create({
          title: 'Free Forever',
          price: 0,
          period: 'freely',
          features: [
            'Full access — never expires',
            'Scheduling', 'Confirmations', 'Medical History',
            'Odontogram', 'Technical Support', 'Patient Management',
            'Prescriptions', 'Digital Signature', 'PDF Export', 'Admin granted'
          ]
        });
        console.log('✓ Free Forever plan seeded');
      }
    } catch (err) {
      console.error('Failed to seed Free Forever plan:', err);
    }
  }
}

const appServer = new AppServer();

// For local development
if (process.env.NODE_ENV !== 'production') {
  appServer.start().catch((err) => {
    console.error('Failed to start server:', err);
    process.exit(1);
  });
}

// Middleware to ensure DB connection for each request in serverless
const ensureDBConnection = async (req: Request, res: Response, next: NextFunction) => {
  try {
    if (!cachedConnection) {
      await appServer['connectDB']();
    }
    next();
  } catch (err) {
    console.error('Database connection error:', err);
    res.status(503).json({ success: false, error: 'Database connection failed' });
  }
};

appServer.app.use(ensureDBConnection);

// Export the app for serverless environments (Lambda, Vercel, etc.)
export default appServer.app;

// Also export the server instance for flexibility
export { appServer };
