import mongoose from "mongoose";
import logger from "../utils/logger.js";

// Database configuration
const dbConfig = {
  uri: process.env.MONGODB_URI || "mongodb://localhost:27017/samaa",
  options: {
    // Connection pool settings
    maxPoolSize: parseInt(process.env.DB_MAX_POOL_SIZE || "10"), // Maximum number of connections in the pool
    minPoolSize: parseInt(process.env.DB_MIN_POOL_SIZE || "2"), // Minimum number of connections in the pool
    maxIdleTimeMS: parseInt(process.env.DB_MAX_IDLE_TIME || "30000"), // Close connections after 30 seconds of inactivity

    // Connection timeout settings
    serverSelectionTimeoutMS: parseInt(
      process.env.DB_SERVER_SELECTION_TIMEOUT || "5000"
    ), // How long to try selecting a server
    socketTimeoutMS: parseInt(process.env.DB_SOCKET_TIMEOUT || "45000"), // How long to wait for a socket
    connectTimeoutMS: parseInt(process.env.DB_CONNECT_TIMEOUT || "10000"), // How long to wait for initial connection

    // Retry settings
    retryWrites: true,
    retryReads: true,

    // Write concern
    w: "majority",
    wtimeoutMS: 5000,

    // Other options
    bufferCommands: false, // Disable mongoose buffering
  },
};

// Connection state
let isConnected = false;
let connectionAttempts = 0;
const MAX_RECONNECTION_ATTEMPTS = 5;
const RECONNECTION_DELAY = 5000; // 5 seconds

/**
 * Handle MongoDB connection events
 */
const setupConnectionEvents = () => {
  mongoose.connection.on("connected", () => {
    isConnected = true;
    connectionAttempts = 0;
    logger.info("MongoDB connected successfully", {
      host: mongoose.connection.host,
      port: mongoose.connection.port,
      database: mongoose.connection.name,
      readyState: mongoose.connection.readyState,
    });
  });

  mongoose.connection.on("error", (error) => {
    isConnected = false;
    logger.error("MongoDB connection error", error);
  });

  mongoose.connection.on("disconnected", () => {
    isConnected = false;
    logger.warn("MongoDB disconnected", {
      readyState: mongoose.connection.readyState,
    });
  });

  mongoose.connection.on("reconnected", () => {
    isConnected = true;
    logger.info("MongoDB reconnected successfully");
  });

  mongoose.connection.on("connecting", () => {
    logger.debug("MongoDB connecting...", {
      uri: dbConfig.uri.replace(/\/\/([^:]+):([^@]+)@/, "//***:***@"), // Hide credentials
    });
  });

  mongoose.connection.on("close", () => {
    isConnected = false;
    logger.warn("MongoDB connection closed");
  });

  // Handle process termination
  process.on("SIGINT", gracefulShutdown);
  process.on("SIGTERM", gracefulShutdown);
};

/**
 * Connect to MongoDB with retry logic
 */
const connectWithRetry = async () => {
  try {
    await mongoose.connect(dbConfig.uri, dbConfig.options);
    return true;
  } catch (error) {
    connectionAttempts++;

    if (connectionAttempts >= MAX_RECONNECTION_ATTEMPTS) {
      logger.error(
        `Failed to connect to MongoDB after ${MAX_RECONNECTION_ATTEMPTS} attempts`,
        error
      );
      throw error;
    }

    logger.warn(
      `MongoDB connection attempt ${connectionAttempts} failed. Retrying in ${
        RECONNECTION_DELAY / 1000
      } seconds...`,
      {
        error: error.message,
        attempts: connectionAttempts,
        maxAttempts: MAX_RECONNECTION_ATTEMPTS,
      }
    );

    // Wait before retrying
    await new Promise((resolve) => setTimeout(resolve, RECONNECTION_DELAY));

    // Retry connection
    return connectWithRetry();
  }
};

/**
 * Connect to MongoDB database
 */
export const connectDB = async () => {
  try {
    // Setup connection events
    setupConnectionEvents();

    // Check if already connected
    if (mongoose.connection.readyState === 1) {
      logger.info("MongoDB already connected");
      return mongoose.connection;
    }

    // Attempt connection with retry
    await connectWithRetry();

    return mongoose.connection;
  } catch (error) {
    logger.error("Failed to establish MongoDB connection", error);
    throw error;
  }
};

/**
 * Disconnect from MongoDB database
 */
export const disconnectDB = async () => {
  try {
    if (mongoose.connection.readyState !== 0) {
      await mongoose.connection.close();
      logger.info("MongoDB disconnected gracefully");
    }
  } catch (error) {
    logger.error("Error disconnecting from MongoDB", error);
    throw error;
  }
};

/**
 * Graceful shutdown handler
 */
const gracefulShutdown = async () => {
  logger.info("Received shutdown signal. Closing MongoDB connection...");

  try {
    await disconnectDB();
    logger.info("MongoDB connection closed. Exiting...");
    process.exit(0);
  } catch (error) {
    logger.error("Error during graceful shutdown", error);
    process.exit(1);
  }
};

/**
 * Check database connection health
 */
export const checkDBHealth = () => {
  try {
    // Check if mongoose is initialized
    if (!mongoose.connection || mongoose.connection.readyState === undefined) {
      return {
        status: "unhealthy",
        state: "not_initialized",
        isConnected: false,
        host: null,
        port: null,
        database: null,
        readyState: -1,
      };
    }

    const state = mongoose.connection.readyState;
    const states = {
      0: "disconnected",
      1: "connected",
      2: "connecting",
      3: "disconnecting",
    };

    return {
      status: state === 1 ? "healthy" : "unhealthy",
      state: states[state] || "unknown",
      isConnected: state === 1,
      host: mongoose.connection.host || null,
      port: mongoose.connection.port || null,
      database: mongoose.connection.name || null,
      readyState: state,
    };
  } catch (error) {
    const errorMsg = error instanceof Error ? error.message : String(error);
    return {
      status: "error",
      state: "error",
      isConnected: false,
      error: errorMsg,
      host: null,
      port: null,
      database: null,
      readyState: -1,
    };
  }
};

/**
 * Get database connection statistics
 */
export const getDBStats = () => {
  const connection = mongoose.connection;

  return {
    host: connection.host,
    port: connection.port,
    database: connection.name,
    readyState: connection.readyState,
    collections: Object.keys(connection.collections || {}).length,
    models: Object.keys(connection.models || {}).length,
  };
};

// Export configuration
export default dbConfig;
