import { makeAutoObservable } from 'mobx';

/**
 * Speech to text class
 * uses webkitSpeechRecognition
 */
export class SpeechToText {
  query: string | null;
  recognition?: SpeechRecognition;
  listening: boolean;
  error?: SpeechRecognitionErrorEvent | Error;
  initialized: boolean;

  constructor() {
    this.query = null;
    this.listening = false;
    this.error = undefined;
    this.initialized = false;
    if (!('webkitSpeechRecognition' in window)) {
      this.upgrade();
    }

    makeAutoObservable(this);
  }

  /**
   * initialize the speech recognition
   * @param continuous - if true, the speech recognition will continue listening after each recognition
   */
  init = (continuous: boolean) => {
    if (!this.initialized) {
      this.recognition = new webkitSpeechRecognition();
      this.setInitialized(true);
    }
    // ensure we only initialize once
    if (this.recognition) {
      // Set the language to use
      this.recognition.continuous = continuous;
      this.recognition.lang = 'en-US';
      // Define a function to handle recognition results
      this.recognition.onresult = this.onResult;
      this.recognition.onstart = this.onstart;
      this.recognition.onend = this.onend;
      this.recognition.onerror = this.onerror;
    }
  };

  /**
   * Sets query - the last result from the speech recognition
   * @param query - the query to set
   */
  setQuery = (query: string | null) => {
    this.query = query;
  };

  /**
   * Upgrade the speech recognition
   */
  upgrade = () => {
    console.log('Feature only available in chrome');
  };

  /**
   *
   * @param event - the speech recognition event
   */
  onResult = (event: SpeechRecognitionEvent) => {
    // Get the last result
    const result = event.results[event.resultIndex];
    // Get the transcript
    const transcript = result[0].transcript; // Do something with the transcript
    this.setQuery(transcript);
  };

  /**
   * Start the speech recognition
   */
  start = () => {
    try {
      if (!this.listening) {
        this.recognition?.start();
      }
    } catch (e) {
      if (e instanceof Error) {
        this.setError(e);
      }
    }
  };

  /**
   * Stop the speech recognition
   */
  stop = () => {
    try {
      this.recognition?.stop();
    } catch (e) {
      if (e instanceof Error) {
        this.setError(e);
      }
    }
  };

  /**
   * mark speech recognition as initialized
   * @param initialized - set the initialized state
   */
  setInitialized = (initialized: boolean) => {
    this.initialized = initialized;
  };

  /**
   * Sets the listening state
   * @param listening - set the listening state
   */
  setListening = (listening: boolean) => {
    this.listening = listening;
  };

  /**
   *
   * @param error - set the error
   */
  setError = (error: SpeechRecognitionErrorEvent | Error) => {
    this.error = error;
  };

  /**
   * Called when the speech recognition starts
   */
  onstart = () => {
    this.setListening(true);
  };

  /**
   * Called when the speech recognition ends
   */
  onend = () => {
    this.setListening(false);
  };

  /**
   * Called when the speech recognition errors
   * @param error - the error
   */
  onerror = (error: SpeechRecognitionErrorEvent) => {
    this.setListening(false);
    this.setError(error);
  };

  /**
   * Cancel the speech recognition
   */
  cancel = () => {
    this.recognition?.abort();
    this.recognition = undefined;
    this.setInitialized(false);
  };

  /**
   * Restart the speech recognition
   * @param continuous - if true, the speech recognition will continue
   * listening after each recognition
   */
  restart = (continuous = true) => {
    if (this.initialized) {
      this.cancel();
    }

    if (this.recognition === undefined) {
      this.init(continuous);
    }
  };
}
