import mongoose from "mongoose";

/**
 * MongoDB Connection Singleton
 * Handles connection pooling and caching for serverless environments
 */

// Declare global mongoose cache type
declare global {
  // eslint-disable-next-line no-var
  var mongooseCache: {
    conn: typeof mongoose | null;
    promise: Promise<typeof mongoose> | null;
  };
}

const MONGODB_URI = process.env.MONGODB_URI;
const MONGODB_DB_NAME = process.env.MONGODB_DB_NAME;
const shouldLog = process.env.NODE_ENV !== "production";

/**
 * Connections this process may hold open.
 *
 * One pool is shared by everything the process serves — page renders, API
 * routes, webhooks, cron — so it is the ceiling on concurrency, not a
 * per-feature setting. When it is exhausted, requests queue inside the driver
 * and the wait shows up as latency on *every* surface at once, which reads
 * like "the whole site got slow" rather than "the database is busy".
 *
 * 50 suits a single app instance against a normal Mongo tier. Raise it for
 * heavier traffic (a mobile client on the same API, several app instances),
 * but keep `instances × MONGODB_MAX_POOL_SIZE` under the connection limit of
 * your Mongo plan — Atlas M0 allows 500, M10 allows 1500.
 */
const DEFAULT_MAX_POOL_SIZE = 50;

function resolveMaxPoolSize(): number {
  const raw = Number.parseInt(process.env.MONGODB_MAX_POOL_SIZE || "", 10);
  if (!Number.isFinite(raw) || raw < 1) return DEFAULT_MAX_POOL_SIZE;
  return raw;
}

// Initialize the cached connection
let cached = global.mongooseCache;

if (!cached) {
  cached = global.mongooseCache = { conn: null, promise: null };
}

/**
 * Connect to MongoDB
 * Uses connection pooling and caching for optimal performance
 */
export async function connectDB(): Promise<typeof mongoose> {
  // Return cached connection if available
  if (cached.conn) {
    return cached.conn;
  }

  // Create new connection if no promise exists
  if (!cached.promise) {
    const opts: mongoose.ConnectOptions = {
      bufferCommands: false,
      ...(MONGODB_DB_NAME ? { dbName: MONGODB_DB_NAME } : {}),
      maxPoolSize: resolveMaxPoolSize(),
      serverSelectionTimeoutMS: 5000,
      socketTimeoutMS: 45000,
      // Large stores should set MONGODB_AUTO_INDEX=false and manage index
      // changes via the migration scripts (scripts/migrate-*.mjs): implicit
      // createIndex on cold starts can trigger surprise index builds on big
      // collections. Left ON by default so fresh installs still get their
      // indexes (including the unique ones) without an extra setup step.
      autoIndex: process.env.MONGODB_AUTO_INDEX !== "false",
    };

    if (!MONGODB_URI) {
      throw new Error(
        "Please define the MONGODB_URI environment variable inside .env"
      );
    }

    cached.promise = mongoose.connect(MONGODB_URI, opts).then((mongoose) => {
      if (shouldLog) {
        console.log("MongoDB connected");
      }
      return mongoose;
    });
  }

  try {
    cached.conn = await cached.promise;
  } catch (error) {
    cached.promise = null;
    console.error("MongoDB connection error:", error);
    throw error;
  }

  return cached.conn;
}

/**
 * Disconnect from MongoDB
 * Useful for graceful shutdown
 */
export async function disconnectDB(): Promise<void> {
  if (cached.conn) {
    await mongoose.disconnect();
    cached.conn = null;
    cached.promise = null;
    if (shouldLog) {
      console.log("MongoDB disconnected");
    }
  }
}

/**
 * Check if MongoDB is connected
 */
export function isConnected(): boolean {
  return mongoose.connection.readyState === 1;
}

// Export mongoose for schema creation
export { mongoose };
