import * as plugins from '../plugins.js';
import { logger } from '../logger.js';
import { defaultTsmDbPath } from '../paths.js';

/**
 * Configuration options for the unified DCRouter database
 */
export interface IDcRouterDbConfig {
  /** External MongoDB connection URL. If absent, uses embedded LocalSmartDb. */
  mongoDbUrl?: string;
  /** Storage path for embedded LocalSmartDb data (default: ~/.serve.zone/dcrouter/tsmdb) */
  storagePath?: string;
  /** Database name (default: dcrouter) */
  dbName?: string;
  /** Enable debug logging */
  debug?: boolean;
}

/**
 * DcRouterDb - Unified database layer for DCRouter
 *
 * Replaces both StorageManager (flat-file key-value) and CacheDb (embedded MongoDB).
 * All data is stored as smartdata document classes in a single database.
 *
 * Two modes:
 * - **Embedded** (default): Spawns a LocalSmartDb (Rust-based MongoDB-compatible engine)
 * - **External**: Connects to a provided MongoDB URL
 */
export class DcRouterDb {
  private static instance: DcRouterDb | null = null;

  private localSmartDb: plugins.smartdb.LocalSmartDb | null = null;
  private smartdataDb!: plugins.smartdata.SmartdataDb;
  private options: Required<IDcRouterDbConfig>;
  private isStarted: boolean = false;

  constructor(options: IDcRouterDbConfig = {}) {
    this.options = {
      mongoDbUrl: options.mongoDbUrl || '',
      storagePath: options.storagePath || defaultTsmDbPath,
      dbName: options.dbName || 'dcrouter',
      debug: options.debug || false,
    };
  }

  /**
   * Get or create the singleton instance
   */
  public static getInstance(options?: IDcRouterDbConfig): DcRouterDb {
    if (!DcRouterDb.instance) {
      DcRouterDb.instance = new DcRouterDb(options);
    }
    return DcRouterDb.instance;
  }

  /**
   * Reset the singleton instance (useful for testing)
   */
  public static resetInstance(): void {
    DcRouterDb.instance = null;
  }

  /**
   * Start the database
   * - If mongoDbUrl is provided, connects directly to external MongoDB
   * - Otherwise, starts an embedded LocalSmartDb instance
   */
  public async start(): Promise<void> {
    if (this.isStarted) {
      logger.log('warn', 'DcRouterDb already started');
      return;
    }

    try {
      let connectionUri: string;

      if (this.options.mongoDbUrl) {
        // External MongoDB mode
        connectionUri = this.options.mongoDbUrl;
        logger.log('info', `DcRouterDb connecting to external MongoDB`);
      } else {
        // Embedded LocalSmartDb mode
        await plugins.fsUtils.ensureDir(this.options.storagePath);

        this.localSmartDb = new plugins.smartdb.LocalSmartDb({
          folderPath: this.options.storagePath,
        });

        const connectionInfo = await this.localSmartDb.start();
        connectionUri = connectionInfo.connectionUri;

        if (this.options.debug) {
          logger.log('debug', `LocalSmartDb started with URI: ${connectionUri}`);
        }

        logger.log('info', `DcRouterDb started embedded instance at ${this.options.storagePath}`);
      }

      // Initialize smartdata ORM
      this.smartdataDb = new plugins.smartdata.SmartdataDb({
        mongoDbUrl: connectionUri,
        mongoDbName: this.options.dbName,
      });
      await this.smartdataDb.init();

      this.isStarted = true;
      logger.log('info', `DcRouterDb ready (db: ${this.options.dbName})`);
    } catch (error: unknown) {
      logger.log('error', `Failed to start DcRouterDb: ${(error as Error).message}`);
      throw error;
    }
  }

  /**
   * Stop the database
   */
  public async stop(): Promise<void> {
    if (!this.isStarted) {
      return;
    }

    try {
      // Close smartdata connection
      if (this.smartdataDb) {
        await this.smartdataDb.close();
      }

      // Stop embedded LocalSmartDb if running
      if (this.localSmartDb) {
        await this.localSmartDb.stop();
        this.localSmartDb = null;
      }

      this.isStarted = false;
      logger.log('info', 'DcRouterDb stopped');
    } catch (error: unknown) {
      logger.log('error', `Error stopping DcRouterDb: ${(error as Error).message}`);
      throw error;
    }
  }

  /**
   * Get the smartdata database instance for @Collection decorators
   */
  public getDb(): plugins.smartdata.SmartdataDb {
    if (!this.isStarted) {
      throw new Error('DcRouterDb not started. Call start() first.');
    }
    return this.smartdataDb;
  }

  /**
   * Check if the database is ready
   */
  public isReady(): boolean {
    return this.isStarted;
  }

  /**
   * Whether running in embedded mode (LocalSmartDb) vs external MongoDB
   */
  public isEmbedded(): boolean {
    return !this.options.mongoDbUrl;
  }

  /**
   * Get the storage path (only relevant for embedded mode)
   */
  public getStoragePath(): string {
    return this.options.storagePath;
  }

  /**
   * Get the database name
   */
  public getDbName(): string {
    return this.options.dbName;
  }
}
