import mongoose from "mongoose";
import { EventEmitter } from "events";
import { IDatabaseAdapter, IUrl } from "../interfaces/database.interface";

interface MongoConfig {
  mongoUri: string;
  maxRetries?: number;
  retryDelay?: number;
  collectionName?: string;
}

export class MongoAdapter extends EventEmitter implements IDatabaseAdapter {
  private mongoUri: string;
  private maxRetries: number;
  private retryDelay: number;
  private collectionName: string;
  private retryCount: number = 0;
  private _isConnected: boolean = false;

  constructor(config: MongoConfig) {
    super();
    this.mongoUri = config.mongoUri;
    this.maxRetries = config.maxRetries || 5;
    this.retryDelay = config.retryDelay || 5000;
    this.collectionName = config.collectionName || "urls";

    // Configure mongoose for high performance
    mongoose.set("strictQuery", true);
  }

  async connect(): Promise<boolean> {
    try {
      if (this._isConnected) {
        return true;
      }

      // Optimized connection options for high throughput
      await mongoose.connect(this.mongoUri, {
        serverSelectionTimeoutMS: 5000,
        maxPoolSize: 100, // Increased connection pool for high throughput
      });

      this._isConnected = true;
      this.retryCount = 0;
      this.emit("connected");

      // Handle connection events
      mongoose.connection.on("error", (err: Error) => {
        console.error("MongoDB connection error:", err);
        this._isConnected = false;
        this.emit("error", err);
        this.retryConnection();
      });

      mongoose.connection.on("disconnected", () => {
        this._isConnected = false;
        this.emit("disconnected");
        this.retryConnection();
      });

      return true;
    } catch (error) {
      console.error("Failed to connect to MongoDB:", error);
      this.emit("error", error);
      return this.retryConnection();
    }
  }

  private retryConnection(): boolean {
    if (this.retryCount < this.maxRetries) {
      this.retryCount++;

      // Exponential backoff
      const delay = this.retryDelay * Math.pow(2, this.retryCount - 1);
      console.log(
        `Retrying MongoDB connection in ${delay}ms (attempt ${this.retryCount}/${this.maxRetries})`
      );

      setTimeout(() => {
        this.connect();
      }, delay);

      return true;
    } else {
      this.emit("maxRetriesReached");
      return false;
    }
  }

  async disconnect(): Promise<void> {
    if (mongoose.connection.readyState !== 0) {
      await mongoose.disconnect();
      this._isConnected = false;
      this.emit("disconnected");
    }
  }

  isConnected(): boolean {
    return this._isConnected;
  }

  async createUrl(url: Omit<IUrl, "createdAt">): Promise<IUrl> {
    const { createUrlModel } = await import("../models/url.model");
    const UrlModel = createUrlModel(this.collectionName);
    const newUrl = new UrlModel({
      urlId: url.urlId,
      originalUrl: url.originalUrl,
      shortUrl: url.shortUrl,
      clicks: url.clicks,
      expiresAt: url.expiresAt,
    });

    const savedUrl = await newUrl.save();
    return {
      urlId: savedUrl.urlId,
      originalUrl: savedUrl.originalUrl,
      shortUrl: savedUrl.shortUrl,
      clicks: savedUrl.clicks,
      createdAt: savedUrl.createdAt,
      expiresAt: savedUrl.expiresAt,
    };
  }

  async findUrlByUrlId(urlId: string): Promise<IUrl | null> {
    const { createUrlModel } = await import("../models/url.model");
    const UrlModel = createUrlModel(this.collectionName);
    const url = await UrlModel.findOne({ urlId }).exec();

    if (!url) {
      return null;
    }

    return {
      urlId: url.urlId,
      originalUrl: url.originalUrl,
      shortUrl: url.shortUrl,
      clicks: url.clicks,
      createdAt: url.createdAt,
      expiresAt: url.expiresAt,
    };
  }

  async findUrlByOriginalUrl(originalUrl: string): Promise<IUrl | null> {
    const { createUrlModel } = await import("../models/url.model");
    const UrlModel = createUrlModel(this.collectionName);
    const url = await UrlModel.findOne({ originalUrl }).exec();

    if (!url) {
      return null;
    }

    return {
      urlId: url.urlId,
      originalUrl: url.originalUrl,
      shortUrl: url.shortUrl,
      clicks: url.clicks,
      createdAt: url.createdAt,
      expiresAt: url.expiresAt,
    };
  }

  async updateUrlClicks(urlId: string, clicks: number): Promise<void> {
    const { createUrlModel } = await import("../models/url.model");
    const UrlModel = createUrlModel(this.collectionName);
    await UrlModel.updateOne({ urlId }, { clicks });
  }

  async deleteUrl(urlId: string): Promise<boolean> {
    const { createUrlModel } = await import("../models/url.model");
    const UrlModel = createUrlModel(this.collectionName);
    const result = await UrlModel.deleteOne({ urlId });
    return result.deletedCount > 0;
  }
}
