// DaVoiceTTSBridge.m
#import "DaVoiceTTSBridge.h"
#import <React/RCTLog.h>
#import <React/RCTConvert.h>

@interface DaVoiceTTSBridge()
@property (nonatomic, strong, nullable) DaVoiceTTS *ttsInstance;
@property (nonatomic, assign) BOOL hasListeners;
@end

@implementation DaVoiceTTSBridge

RCT_EXPORT_MODULE();

+ (BOOL)requiresMainQueueSetup {
    RCTLogInfo(@"[DaVoiceTTSBridge] requiresMainQueueSetup called (module loaded).");
  NSLog(@"[DaVoiceTTSBridge] requiresMainQueueSetup called (module loaded).");

  return YES;
}

#pragma mark - RCTEventEmitter

- (NSArray<NSString *> *)supportedEvents
{
  // Single event exposed to JS.
  return @[@"onFinishedSpeaking"];
}

- (void)startObserving
{
  self.hasListeners = YES;
}

- (void)stopObserving
{
  self.hasListeners = NO;
}

- (void)dealloc
{
  if (_ttsInstance) {
    [_ttsInstance destroy];
    _ttsInstance = nil;
  }
}

#pragma mark - API

- (instancetype)init {
  self = [super init];
  if (self) {
    RCTLogInfo(@"[DaVoiceTTSBridge] init called.");
    NSLog(@"[DaVoiceTTSBridge] init called.");
  }
  return self;
}

RCT_EXPORT_METHOD(initTTS:(NSDictionary *)configDict
                  resolver:(RCTPromiseResolveBlock)resolve
                  rejecter:(RCTPromiseRejectBlock)reject)
{
  RCTLogInfo(@"[DaVoiceTTSBridge] initTTS called with configDict: %@", configDict);
  NSLog(@"[DaVoiceTTSBridge] initTTS called with configDict: %@", configDict);
  NSString *modelPath = configDict[@"model"];

  if (!modelPath) {
    reject(@"invalid_config", @"Missing required 'model' key", nil);
    return;
  }

  NSURL *modelURL = [NSURL fileURLWithPath:modelPath];

  NSError *error = nil;
  // Ensure we touch AVAudio/ORT from main.
  dispatch_async(dispatch_get_main_queue(), ^{
      NSError *error = nil;
      self.ttsInstance = [[DaVoiceTTS alloc] initWithModel:modelURL error:&error];
      if (error || !self.ttsInstance) {
        reject(@"init_failed", error.localizedDescription ?: @"Failed to initialize", error);
        return;
      }

      __weak typeof(self) weakSelf = self;
      // Bridge Swift callback -> RN event
      self.ttsInstance.onLastUtteranceFinished = ^{
        __strong typeof(weakSelf) strongSelf = weakSelf;
        if (!strongSelf) return;
        if (!strongSelf.hasListeners) return;

        // Send on main just to be extra safe for JS listeners doing UI work.
        dispatch_async(dispatch_get_main_queue(), ^{
          [strongSelf sendEventWithName:@"onFinishedSpeaking" body:@{}];
        });
      };

      resolve(@"TTS initialized");
    });
  }

  RCT_EXPORT_METHOD(speak:(NSString *)text
                    speakerId:(nonnull NSNumber *)speakerId
                    resolver:(RCTPromiseResolveBlock)resolve
                    rejecter:(RCTPromiseRejectBlock)reject)
  {
    if (!self.ttsInstance) {
      reject(@"no_instance", @"TTS not initialized", nil);
      return;
    }

    dispatch_async(dispatch_get_main_queue(), ^{
      [self.ttsInstance speak:text sid:speakerId.intValue];
      resolve(@"Speaking");
    });
  }

  RCT_EXPORT_METHOD(stopSpeaking:(RCTPromiseResolveBlock)resolve
                    rejecter:(RCTPromiseRejectBlock)reject)
  {
    if (!self.ttsInstance) {
      reject(@"no_instance", @"TTS not initialized", nil);
      return;
    }

    dispatch_async(dispatch_get_main_queue(), ^{
      [self.ttsInstance stopSpeaking];
      resolve(@"Stopped");
    });
  }

  RCT_EXPORT_METHOD(destroy:(RCTPromiseResolveBlock)resolve
                    rejecter:(RCTPromiseRejectBlock)reject)
  {
    if (!self.ttsInstance) {
      // Idempotent: resolve anyway.
      resolve(@"Already destroyed");
      return;
    }

    dispatch_async(dispatch_get_main_queue(), ^{
      [self.ttsInstance destroy];
      self.ttsInstance = nil;
      resolve(@"Destroyed");
    });
  }

@end
