import * as plugins from '../plugins.ts';
import { logger } from '../logging.ts';

export interface ICacheDbOptions {
  storagePath?: string;
  dbName?: string;
  debug?: boolean;
}

/**
 * Singleton wrapper around LocalTsmDb + SmartdataDb.
 * Provides a managed MongoDB-compatible cache database.
 */
export class CacheDb {
  private static instance: CacheDb | null = null;

  private smartMongo: InstanceType<typeof plugins.smartmongo.SmartMongo> | null = null;
  private smartdataDb: InstanceType<typeof plugins.smartdata.SmartdataDb> | null = null;
  private options: Required<ICacheDbOptions>;

  private constructor(options: ICacheDbOptions = {}) {
    this.options = {
      storagePath: options.storagePath ?? './.nogit/cachedb',
      dbName: options.dbName ?? 'gitops_cache',
      debug: options.debug ?? false,
    };
  }

  static getInstance(options?: ICacheDbOptions): CacheDb {
    if (!CacheDb.instance) {
      CacheDb.instance = new CacheDb(options);
    }
    return CacheDb.instance;
  }

  static resetInstance(): void {
    CacheDb.instance = null;
  }

  async start(): Promise<void> {
    logger.info('Starting CacheDb...');
    this.smartMongo = await plugins.smartmongo.SmartMongo.createAndStart();
    const mongoDescriptor = await this.smartMongo.getMongoDescriptor();

    this.smartdataDb = new plugins.smartdata.SmartdataDb({
      mongoDbUrl: mongoDescriptor.mongoDbUrl,
      mongoDbName: this.options.dbName,
    });
    await this.smartdataDb.init();
    logger.success(`CacheDb started (db: ${this.options.dbName})`);
  }

  async stop(): Promise<void> {
    logger.info('Stopping CacheDb...');
    if (this.smartdataDb) {
      await this.smartdataDb.close();
      this.smartdataDb = null;
    }
    if (this.smartMongo) {
      // smartMongo.stop() may hang under Deno — fire-and-forget with timeout
      const stopPromise = this.smartMongo.stop().catch(() => {});
      await Promise.race([
        stopPromise,
        new Promise<void>((resolve) => {
          const id = setTimeout(resolve, 3000);
          Deno.unrefTimer(id);
        }),
      ]);
      this.smartMongo = null;
    }
    logger.success('CacheDb stopped');
  }

  getDb(): InstanceType<typeof plugins.smartdata.SmartdataDb> {
    if (!this.smartdataDb) {
      throw new Error('CacheDb not started. Call start() first.');
    }
    return this.smartdataDb;
  }
}
