import 'package:flutter/foundation.dart';
import 'package:speech_to_text/speech_recognition_error.dart';
import 'package:speech_to_text/speech_to_text.dart' as stt;

typedef DictationResultCallback =
    void Function(String words, bool isFinalResult);
typedef DictationStatusCallback = void Function(bool isListening);
typedef DictationErrorCallback = void Function(String errorCode);

class SpeechDictationService {
  SpeechDictationService._();

  static final SpeechDictationService instance = SpeechDictationService._();

  final stt.SpeechToText _speech = stt.SpeechToText();

  DictationResultCallback? _onResult;
  DictationStatusCallback? _onStatusChanged;
  DictationErrorCallback? _onError;

  bool get isListening => _speech.isListening;

  Future<bool> initialize({
    required DictationStatusCallback onStatusChanged,
    required DictationErrorCallback onError,
  }) async {
    _onStatusChanged = onStatusChanged;
    _onError = onError;

    if (_speech.isAvailable) return true;

    return _speech.initialize(
      onStatus: _handleStatus,
      onError: _handleError,
      debugLogging: kDebugMode,
      options: [stt.SpeechToText.androidNoBluetooth],
    );
  }

  Future<void> listen({required DictationResultCallback onResult}) async {
    _onResult = onResult;
    final localeId = await _preferredSpanishLocaleId();

    await _speech.listen(
      onResult: (result) =>
          _onResult?.call(result.recognizedWords, result.finalResult),
      listenOptions: stt.SpeechListenOptions(
        cancelOnError: true,
        partialResults: true,
        listenMode: stt.ListenMode.dictation,
        autoPunctuation: true,
        pauseFor: const Duration(seconds: 8),
        listenFor: const Duration(minutes: 2),
        localeId: localeId,
      ),
    );
  }

  Future<void> stop() async {
    await _speech.stop();
    // Android puede entregar el resultado final poco después de informar que
    // dejó de escuchar. Esta breve espera permite conservar esas últimas
    // palabras antes de cerrar el receptor.
    await Future<void>.delayed(const Duration(milliseconds: 300));
    _onResult = null;
    _onStatusChanged?.call(false);
  }

  Future<void> endSession() async {
    _onResult = null;
    _onStatusChanged = null;
    _onError = null;
    if (_speech.isAvailable) {
      await _speech.cancel();
    }
  }

  void _handleStatus(String status) {
    if (status == stt.SpeechToText.listeningStatus) {
      _onStatusChanged?.call(true);
    } else if (status == stt.SpeechToText.doneStatus ||
        status == stt.SpeechToText.notListeningStatus) {
      _onStatusChanged?.call(false);
    }
  }

  void _handleError(SpeechRecognitionError error) {
    _onResult = null;
    _onError?.call(error.errorMsg);
    _onStatusChanged?.call(false);
  }

  Future<String?> _preferredSpanishLocaleId() async {
    try {
      final locales = await _speech.locales();
      for (final locale in locales) {
        final normalized = locale.localeId.toLowerCase().replaceAll('_', '-');
        if (normalized == 'es-cl') return locale.localeId;
      }
      for (final locale in locales) {
        if (locale.localeId.toLowerCase().startsWith('es')) {
          return locale.localeId;
        }
      }
    } catch (_) {
      // El reconocedor usará el idioma predeterminado del dispositivo.
    }
    return null;
  }
}
