import { NativeAudioAPIModule } from '../specs';
import type {
  WorkletRuntime,
  IAudioAPIModule,
  IWorkletsModule,
} from './ModuleInterfaces';
import { AudioApiError } from '../errors';
import semverGte from 'semver/functions/gte';

class AudioAPIModule implements IAudioAPIModule {
  #workletsModule_: IWorkletsModule | null = null;
  #canUseWorklets_ = false;
  #workletsVersion = 'unknown';
  #workletsAvailable_ = false;
  private static readonly MIN_WORKLETS_VERSION = '0.6.0';

  constructor() {
    // Important! Verify and import worklets first
    // otherwise the native module installation might crash
    // if react-native-worklets is not imported before audio-api
    this.#verifyWorklets();

    if (this.#verifyInstallation()) {
      return;
    }

    if (!NativeAudioAPIModule) {
      throw new AudioApiError(
        `Failed to install react-native-audio-api: The native module could not be found.`
      );
    }

    NativeAudioAPIModule.install();
  }

  #verifyWorklets(): boolean {
    try {
      const workletsPackage = require('react-native-worklets');
      const workletsPackageJson = require('react-native-worklets/package.json');
      this.#workletsVersion = workletsPackageJson.version;
      this.#workletsAvailable_ = true;

      this.#canUseWorklets_ = semverGte(
        this.#workletsVersion,
        AudioAPIModule.MIN_WORKLETS_VERSION
      );

      if (this.#canUseWorklets_) {
        this.#workletsModule_ = workletsPackage;
      }

      return this.#canUseWorklets_;
    } catch {
      this.#canUseWorklets_ = false;
      return false;
    }
  }

  #verifyInstallation(): boolean {
    return (
      globalThis.createAudioContext != null &&
      globalThis.createOfflineAudioContext != null &&
      globalThis.createAudioRecorder != null &&
      globalThis.createAudioBuffer != null &&
      globalThis.createAudioDecoder != null &&
      globalThis.createAudioFileUtils != null &&
      globalThis.AudioEventEmitter != null
    );
  }

  get workletsModule(): IWorkletsModule | null {
    return this.#workletsModule_;
  }

  /**
   * Indicates whether react-native-worklets are installed in matching version,
   * for usage with react-native-audio-api.
   */
  get canUseWorklets(): boolean {
    return this.#canUseWorklets_;
  }

  /** Returns the installed worklets version or 'unknown'. */
  get workletsVersion(): string {
    return this.#workletsVersion;
  }

  /**
   * Indicates whether react-native-worklets are installed, regardless of
   * version support. Useful for asserting compatibility.
   */
  get areWorkletsAvailable(): boolean {
    return this.#workletsAvailable_;
  }

  /**
   * Indicates whether the installed react-native-worklets version is supported.
   * Useful for asserting compatibility.
   */
  get isWorkletsVersionSupported(): boolean {
    // Note: if areWorkletsAvailable is true, canUseWorklets is equivalent to version check
    return this.#canUseWorklets_;
  }

  /** Returns the range of supported worklets versions. */
  get supportedWorkletsVersion(): string[] {
    return [`>=${AudioAPIModule.MIN_WORKLETS_VERSION}`];
  }

  public createAudioRuntime(): WorkletRuntime | null {
    if (!this.#canUseWorklets_) {
      return null;
    }

    return this.#workletsModule_!.createWorkletRuntime('AudioWorkletRuntime');
  }
}

export default new AudioAPIModule();
