require('dotenv').config();
const express = require('express');
const helmet = require('helmet');
const rateLimit = require('express-rate-limit');
const winston = require('winston');
const Joi = require('joi');
const { startBot, sendTextMessage, sendMediaMessage } = require('./bot');

const app = express();
const PORT = process.env.PORT || 3003;

// Configure Winston Logger
const logger = winston.createLogger({
  level: process.env.LOG_LEVEL || 'info',
  format: winston.format.combine(
    winston.format.timestamp(),
    winston.format.errors({ stack: true }),
    winston.format.json()
  ),
  defaultMeta: { service: 'pamsika-whatsapp-bot' },
  transports: [
    new winston.transports.File({
      filename: 'logs/error.log',
      level: 'error',
      maxsize: 5242880, // 5MB
      maxFiles: 5
    }),
    new winston.transports.File({
      filename: 'logs/combined.log',
      maxsize: 5242880, // 5MB
      maxFiles: 10
    })
  ]
});

// Add console logging in development
if (process.env.NODE_ENV !== 'production') {
  logger.add(new winston.transports.Console({
    format: winston.format.simple()
  }));
}

// Security middleware
app.use(helmet({
  contentSecurityPolicy: {
    directives: {
      defaultSrc: ["'self'"],
      styleSrc: ["'self'", "'unsafe-inline'"],
      scriptSrc: ["'self'"],
      imgSrc: ["'self'", "data:", "https:"],
    },
  },
  hsts: {
    maxAge: 31536000,
    includeSubDomains: true,
    preload: true
  }
}));

// Body parsing middleware with size limits
app.use(express.json({
  limit: '10mb',
  verify: (req, res, buf) => {
    req.rawBody = buf;
  }
}));
app.use(express.urlencoded({ extended: true, limit: '10mb' }));

// API Key Authentication Middleware
const authenticateAPI = (req, res, next) => {
  const apiKey = req.headers['x-api-key'] || req.headers['authorization']?.replace('Bearer ', '');

  if (!apiKey) {
    logger.warn('API request without authentication', { ip: req.ip, endpoint: req.path });
    return res.status(401).json({
      success: false,
      error: {
        code: 401,
        message: 'API key required',
        timestamp: new Date().toISOString()
      }
    });
  }

  if (apiKey !== process.env.API_KEY) {
    logger.warn('API request with invalid key', { ip: req.ip, endpoint: req.path });
    return res.status(401).json({
      success: false,
      error: {
        code: 401,
        message: 'Invalid API key',
        timestamp: new Date().toISOString()
      }
    });
  }

  next();
};

// Rate limiting configurations
const createRateLimit = (windowMs, max, message, skipSuccessfulRequests = false) => rateLimit({
  windowMs,
  max,
  message: {
    success: false,
    error: {
      code: 429,
      message,
      timestamp: new Date().toISOString()
    }
  },
  standardHeaders: true,
  legacyHeaders: false,
  skipSuccessfulRequests,
  keyGenerator: (req) => {
    return req.headers['x-forwarded-for'] || req.connection.remoteAddress || req.socket.remoteAddress;
  }
});

// Different rate limits for different endpoints
const strictLimit = createRateLimit(15 * 60 * 1000, 50, 'Too many requests, please slow down');
const normalLimit = createRateLimit(15 * 60 * 1000, 100, 'Rate limit exceeded, please try again later');
const healthLimit = createRateLimit(1 * 60 * 1000, 30, 'Too many health checks');

// Request logging middleware
app.use((req, res, next) => {
  const start = Date.now();

  res.on('finish', () => {
    const duration = Date.now() - start;
    const logData = {
      method: req.method,
      url: req.url,
      status: res.statusCode,
      duration: `${duration}ms`,
      ip: req.ip,
      userAgent: req.get('User-Agent')
    };

    if (res.statusCode >= 400) {
      logger.error('HTTP Error', logData);
    } else {
      logger.info('HTTP Request', logData);
    }
  });

  next();
});

// Validation schemas
const messageSchema = Joi.object({
  phone: Joi.string()
    .pattern(/^[1-9]\d{1,14}$/)
    .required()
    .messages({
      'string.pattern.base': 'Phone number must be in international format (e.g., 265991234567)',
      'any.required': 'Phone number is required'
    }),
  message: Joi.string()
    .min(1)
    .max(4096)
    .required()
    .messages({
      'string.min': 'Message cannot be empty',
      'string.max': 'Message too long (max 4096 characters)',
      'any.required': 'Message is required'
    })
});

const mediaSchema = Joi.object({
  phone: Joi.string()
    .pattern(/^[1-9]\d{1,14}$/)
    .required()
    .messages({
      'string.pattern.base': 'Phone number must be in international format',
      'any.required': 'Phone number is required'
    }),
  mediaUrl: Joi.string()
    .uri()
    .required()
    .messages({
      'string.uri': 'Invalid media URL format',
      'any.required': 'Media URL is required'
    }),
  caption: Joi.string()
    .max(1024)
    .optional()
    .messages({
      'string.max': 'Caption too long (max 1024 characters)'
    })
});

const paymentUpdateSchema = Joi.object({
  orderId: Joi.string()
    .alphanum()
    .min(3)
    .max(50)
    .required()
    .messages({
      'string.alphanum': 'Order ID must contain only letters and numbers',
      'string.min': 'Order ID too short',
      'string.max': 'Order ID too long',
      'any.required': 'Order ID is required'
    }),
  status: Joi.string()
    .valid('Pending', 'Paid', 'Failed', 'Cancelled', 'Refunded')
    .required()
    .messages({
      'any.only': 'Invalid payment status',
      'any.required': 'Payment status is required'
    }),
  phone: Joi.string()
    .pattern(/^[1-9]\d{1,14}$/)
    .required()
    .messages({
      'string.pattern.base': 'Phone number must be in international format',
      'any.required': 'Phone number is required'
    })
});

// Validation middleware factory
const validateSchema = (schema) => {
  return (req, res, next) => {
    const { error, value } = schema.validate(req.body, { abortEarly: false });

    if (error) {
      logger.warn('Validation error', {
        errors: error.details.map(detail => detail.message),
        body: req.body,
        ip: req.ip
      });

      return res.status(400).json({
        success: false,
        error: {
          code: 400,
          message: 'Validation failed',
          details: error.details.map(detail => ({
            field: detail.path.join('.'),
            message: detail.message
          })),
          timestamp: new Date().toISOString()
        }
      });
    }

    req.validatedBody = value;
    next();
  };
};

// Standard error response helper
const createErrorResponse = (message, code = 500, details = null) => ({
  success: false,
  error: {
    code,
    message,
    details,
    timestamp: new Date().toISOString()
  }
});

// Standard success response helper
const createSuccessResponse = (message, data = null) => ({
  success: true,
  message,
  data,
  timestamp: new Date().toISOString()
});

// Health check endpoint (should be first)
app.get('/health', healthLimit, (req, res) => {
  const health = {
    status: 'healthy',
    timestamp: new Date().toISOString(),
    uptime: Math.floor(process.uptime()),
    memory: {
      used: Math.round(process.memoryUsage().heapUsed / 1024 / 1024),
      total: Math.round(process.memoryUsage().heapTotal / 1024 / 1024)
    },
    version: require('./package.json').version,
    environment: process.env.NODE_ENV || 'development'
  };

  // Add WhatsApp connection status if available
  try {
    health.services = {
      whatsapp: global.whatsappConnected ? 'connected' : 'disconnected'
    };
  } catch (error) {
    health.services = { whatsapp: 'unknown' };
  }

  res.json(health);
});

// Start the WhatsApp bot
try {
  startBot();
  logger.info('WhatsApp bot initialization started');
} catch (error) {
  logger.error('Failed to start WhatsApp bot', { error: error.message });
}

// Initialize cron jobs
try {
  require('./cron');
  logger.info('Cron jobs initialized');
} catch (error) {
  logger.error('Failed to initialize cron jobs', { error: error.message });
}

// API Endpoints

// Send text message endpoint
app.post('/send-message',
  authenticateAPI,
  normalLimit,
  validateSchema(messageSchema),
  async (req, res) => {
    try {
      const { phone, message } = req.validatedBody;

      logger.info('Sending text message', { phone, messageLength: message.length });

      await sendTextMessage(phone, message);

      logger.info('Text message sent successfully', { phone });

      res.status(200).json(createSuccessResponse('Message sent successfully'));
    } catch (error) {
      logger.error('Error sending text message', {
        error: error.message,
        stack: error.stack,
        phone: req.validatedBody?.phone
      });

      res.status(500).json(createErrorResponse('Failed to send message'));
    }
  }
);

// Send media message endpoint
app.post('/send-media',
  authenticateAPI,
  strictLimit,
  validateSchema(mediaSchema),
  async (req, res) => {
    try {
      const { phone, mediaUrl, caption } = req.validatedBody;

      logger.info('Sending media message', { phone, mediaUrl, caption });

      await sendMediaMessage(phone, mediaUrl, caption || '');

      logger.info('Media message sent successfully', { phone, mediaUrl });

      res.status(200).json(createSuccessResponse('Media sent successfully'));
    } catch (error) {
      logger.error('Error sending media message', {
        error: error.message,
        stack: error.stack,
        phone: req.validatedBody?.phone,
        mediaUrl: req.validatedBody?.mediaUrl
      });

      res.status(500).json(createErrorResponse('Failed to send media'));
    }
  }
);

// Payment status update endpoint
app.post('/payment-update',
  authenticateAPI,
  normalLimit,
  validateSchema(paymentUpdateSchema),
  async (req, res) => {
    try {
      const { orderId, status, phone } = req.validatedBody;

      logger.info('Processing payment update', { orderId, status, phone });

      const message = `Your order #${orderId} payment status has been updated to: *${status}*.`;

      await sendTextMessage(phone, message);

      logger.info('Payment update notification sent', { orderId, status, phone });

      res.status(200).json(createSuccessResponse('Payment update notification sent'));
    } catch (error) {
      logger.error('Error sending payment update', {
        error: error.message,
        stack: error.stack,
        orderId: req.validatedBody?.orderId,
        phone: req.validatedBody?.phone
      });

      res.status(500).json(createErrorResponse('Failed to send payment update notification'));
    }
  }
);

// 404 handler
app.use('*', (req, res) => {
  logger.warn('404 - Route not found', {
    method: req.method,
    url: req.originalUrl,
    ip: req.ip
  });

  res.status(404).json(createErrorResponse('Route not found', 404));
});

// Global error handler
app.use((error, req, res, next) => {
  logger.error('Unhandled error', {
    error: error.message,
    stack: error.stack,
    url: req.url,
    method: req.method,
    ip: req.ip
  });

  // Don't leak error details in production
  const message = process.env.NODE_ENV === 'production'
    ? 'Internal server error'
    : error.message;

  res.status(500).json(createErrorResponse(message, 500));
});

// Graceful shutdown handling
const gracefulShutdown = (signal) => {
  logger.info(`Received ${signal}, starting graceful shutdown`);

  server.close(() => {
    logger.info('HTTP server closed');

    // Close database connections, cleanup resources, etc.
    process.exit(0);
  });

  // Force shutdown after 30 seconds
  setTimeout(() => {
    logger.error('Forced shutdown due to timeout');
    process.exit(1);
  }, 30000);
};

// Start server
const server = app.listen(PORT, () => {
  logger.info(`Server running on port ${PORT}`, {
    environment: process.env.NODE_ENV || 'development',
    port: PORT
  });
});

// Handle shutdown signals
process.on('SIGTERM', () => gracefulShutdown('SIGTERM'));
process.on('SIGINT', () => gracefulShutdown('SIGINT'));

// Handle uncaught exceptions
process.on('uncaughtException', (error) => {
  logger.error('Uncaught Exception', { error: error.message, stack: error.stack });
  process.exit(1);
});

// Handle unhandled promise rejections
process.on('unhandledRejection', (reason, promise) => {
  logger.error('Unhandled Rejection', { reason, promise });
});

module.exports = app;
