import lodash from 'lodash';
import { parse } from '@lukeed/ms';
import EventEmitter from 'node:events';
import { noopLogger } from 'typescript-log';

import type {
  Duration,
  Emitter,
  FactoryTimeoutOptions,
  GracePeriodOptions as GracePeriodOptions,
  Logger,
  RawMasterCacheOptions,
} from './types/main';

/**
 * The default options to use throughout the library
 *
 * Some of them can be override on a per-cache basis
 * or on a per-operation basis
 */
export class MasterCacheOptions {
  #options: RawMasterCacheOptions;

  /**
   * The default TTL for all caches
   *
   * @default 30m
   */
  ttl: Duration = parse('30m');

  /**
   * Default prefix for all caches
   */
  prefix: string = 'mastercache';

  /**
   * The grace period options
   */
  gracePeriod: GracePeriodOptions = {
    enabled: false,
    duration: parse('6h'),
    fallbackDuration: parse('10s'),
  };

  /**
   * Default early expiration percentage
   */
  earlyExpiration: number = 0;

  /**
   * Whether to suppress L2 cache errors
   */
  suppressL2Errors: boolean = true;

  /**
   * The soft and hard timeouts for the factories
   */
  timeouts?: FactoryTimeoutOptions = {
    soft: null,
    hard: null,
  };

  /**
   * The logger used throughout the library
   */
  logger: Logger = noopLogger();

  /**
   * The emitter used throughout the library
   */
  emitter: Emitter = new EventEmitter();

  /**
   * Max time to wait for the lock to be acquired
   */
  lockTimeout?: Duration = null;

  constructor(options: RawMasterCacheOptions) {
    this.#options = lodash.merge({}, this, options);

    this.prefix = this.#options.prefix!;
    this.ttl = this.#options.ttl!;
    this.timeouts = this.#options.timeouts;
    this.earlyExpiration = this.#options.earlyExpiration!;
    this.suppressL2Errors = this.#options.suppressL2Errors!;
    this.lockTimeout = this.#options.lockTimeout;
    this.gracePeriod = this.#options.gracePeriod!;

    this.emitter = this.#options.emitter!;
    this.logger = this.#options.logger!.child({ pkg: 'mastercache' });
  }

  cloneWith(options: RawMasterCacheOptions) {
    const newOptions = lodash.merge({}, this.#options, options);
    return new MasterCacheOptions(newOptions);
  }
}
