// stt/index.ts
import { NativeModules, NativeEventEmitter, Platform } from 'react-native';

// Prefer new names; gracefully fall back to legacy names during migration.
const STTNative =
  NativeModules.STT ||
  NativeModules.RCTSTT ||
  NativeModules.Voice ||
  NativeModules.RCTVoice;

if (STTNative) {
  console.log('STTNative loaded:', Object.keys(STTNative));
} else {
  console.error('STT native module is not linked correctly.');
}

type SpeechStartEvent = { /* keep your shape if you had TS types */ };
type SpeechEndEvent = {};
type SpeechRecognizedEvent = { isFinal: boolean };
type SpeechErrorEvent = { error: { code?: string; message?: string } };
type SpeechResultsEvent = { value: string[] };
type SpeechVolumeChangeEvent = { value: number };

type SpeechEvents = {
  onSpeechStart?: (e: SpeechStartEvent) => void;
  onSpeechRecognized?: (e: SpeechRecognizedEvent) => void;
  onSpeechEnd?: (e: SpeechEndEvent) => void;
  onSpeechError?: (e: SpeechErrorEvent) => void;
  onSpeechResults?: (e: SpeechResultsEvent) => void;
  onSpeechPartialResults?: (e: SpeechResultsEvent) => void;
  onSpeechVolumeChanged?: (e: SpeechVolumeChangeEvent) => void;
};

const sttEmitter =
  Platform.OS !== 'web' && STTNative ? new NativeEventEmitter(STTNative) : null;

type SpeechEventName = keyof Required<SpeechEvents>;

class RCTSTT {
  private _loaded: boolean;
  private _listeners: Array<{ remove: () => void }> | null;
  private _events: Required<SpeechEvents>;

  constructor() {
    this._loaded = false;
    this._listeners = null;
    this._events = {
      onSpeechStart: () => {},
      onSpeechRecognized: () => {},
      onSpeechEnd: () => {},
      onSpeechError: () => {},
      onSpeechResults: () => {},
      onSpeechPartialResults: () => {},
      onSpeechVolumeChanged: () => {},
    };
  }

  /** Remove static handlers on the native module (parity with old Voice bridge). */
  removeAllListeners() {
    if (!STTNative) return;
    STTNative.onSpeechStart = undefined;
    STTNative.onSpeechRecognized = undefined;
    STTNative.onSpeechEnd = undefined;
    STTNative.onSpeechError = undefined;
    STTNative.onSpeechResults = undefined;
    STTNative.onSpeechPartialResults = undefined;
    STTNative.onSpeechVolumeChanged = undefined;
  }

  /** Destroy/native teardown (parity with old API). */
  destroy(): Promise<void> {
    if (!this._loaded && !this._listeners) {
      return Promise.resolve();
    }
    return new Promise((resolve, reject) => {
      if (!STTNative?.destroySpeech) {
        // Fallback: tolerate older native
        this._listeners?.forEach(l => l.remove?.());
        this._listeners = null;
        resolve();
        return;
      }
      STTNative.destroySpeech((error: string) => {
        if (error) {
          reject(new Error(error));
        } else {
          this._listeners?.forEach(l => l.remove?.());
          this._listeners = null;
          resolve();
        }
      });
    });
  }

  /** Start recognition — mirrors old `.start(locale, options?)` */
  start(locale: string, options: Record<string, any> = {}) {
    if (!this._loaded && !this._listeners && sttEmitter) {
      this._listeners = (Object.keys(this._events) as SpeechEventName[]).map(
        (key: SpeechEventName) => sttEmitter.addListener(key, this._events[key]),
      );
    }
    return new Promise<void>((resolve, reject) => {
      const cb = (error: string) => (error ? reject(new Error(error)) : resolve());

      if (Platform.OS === 'android' && STTNative?.startSpeech) {
        // Keep Android parity with your previous voice bridge contract
        STTNative.startSpeech(
          locale,
          {
            EXTRA_LANGUAGE_MODEL: 'LANGUAGE_MODEL_FREE_FORM',
            EXTRA_MAX_RESULTS: 5,
            EXTRA_PARTIAL_RESULTS: true,
            REQUEST_PERMISSIONS_AUTO: true,
            ...options,
          },
          cb,
        );
      } else if (STTNative?.startSpeech) {
        STTNative.startSpeech(locale, cb);
      } else if (STTNative?.start) {
        STTNative.start(locale, cb);
      } else {
        reject(new Error('STTNative.startSpeech not available'));
      }
    });
  }

  stop(): Promise<void> {
    if (!this._loaded && !this._listeners) return Promise.resolve();
    return new Promise((resolve, reject) => {
      if (!STTNative?.stopSpeech) return resolve();
      STTNative.stopSpeech((error: string) => {
        error ? reject(new Error(error)) : resolve();
      });
    });
  }

  cancel(): Promise<void> {
    if (!this._loaded && !this._listeners) return Promise.resolve();
    return new Promise((resolve, reject) => {
      if (!STTNative?.cancelSpeech) return resolve();
      STTNative.cancelSpeech((error: string) => {
        error ? reject(new Error(error)) : resolve();
      });
    });
  }

  isAvailable(): Promise<0 | 1> {
    return new Promise((resolve, reject) => {
      if (!STTNative?.isSpeechAvailable) {
        // Best effort fallback
        resolve(1);
        return;
      }
      STTNative.isSpeechAvailable((isAvailable: 0 | 1, error: string) => {
        error ? reject(new Error(error)) : resolve(isAvailable);
      });
    });
  }

  isRecognizing(): Promise<0 | 1> {
    return new Promise(resolve => {
      if (!STTNative?.isRecognizing) return resolve(0);
      STTNative.isRecognizing((isRecognizing: 0 | 1) => resolve(isRecognizing));
    });
  }

  // Event setters (unchanged names so your app code stays the same)
  set onSpeechStart(fn: (e: SpeechStartEvent) => void) {
    this._events.onSpeechStart = fn;
  }
  set onSpeechRecognized(fn: (e: SpeechRecognizedEvent) => void) {
    this._events.onSpeechRecognized = fn;
  }
  set onSpeechEnd(fn: (e: SpeechEndEvent) => void) {
    this._events.onSpeechEnd = fn;
  }
  set onSpeechError(fn: (e: SpeechErrorEvent) => void) {
    this._events.onSpeechError = fn;
  }
  set onSpeechResults(fn: (e: SpeechResultsEvent) => void) {
    this._events.onSpeechResults = fn;
  }
  set onSpeechPartialResults(fn: (e: SpeechResultsEvent) => void) {
    this._events.onSpeechPartialResults = fn;
  }
  set onSpeechVolumeChanged(fn: (e: SpeechVolumeChangeEvent) => void) {
    this._events.onSpeechVolumeChanged = fn;
  }
}

export type {
  SpeechEndEvent,
  SpeechErrorEvent,
  SpeechEvents,
  SpeechStartEvent,
  SpeechRecognizedEvent,
  SpeechResultsEvent,
  SpeechVolumeChangeEvent,
};

export default new RCTSTT();
