//ios/KeyWordRNBridge.mm

#import "KeyWordRNBridge.h"
#import <React/RCTBridge.h>
#import <React/RCTLog.h>
#import <React/RCTEventEmitter.h>
//#import "KeyWordsDetection.h" // Import your KeyWordsDetection library header

// Ensure the protocol is correctly imported or declared
// Assuming the protocol is named 'KeywordDetectionRNDelegate'
@interface KeyWordsDetectionWrapper : NSObject <KeywordDetectionRNDelegate>

@property (nonatomic, strong) KeyWordsDetection *keyWordsDetection;
@property (nonatomic, strong) NSString *instanceId;
@property (nonatomic, weak) KeyWordRNBridge *bridge;

- (instancetype)initWithInstanceId:(NSString *)instanceId
                         modelName:(NSString *)modelName
                         threshold:(float)threshold
                         bufferCnt:(NSInteger)bufferCnt
                            bridge:(KeyWordRNBridge *)bridge
                             error:(NSError **)error;

- (instancetype)initWithInstanceId:(NSString *)instanceId
                        modelNames:(NSArray<NSString *> *)modelNames
                        thresholds:(NSArray<NSNumber *> *)thresholds
                        bufferCnts:(NSArray<NSNumber *> *)bufferCnts
                 msBetweenCallback:(NSArray<NSNumber *> *)msBetweenCallback
                            bridge:(KeyWordRNBridge *)bridge
                             error:(NSError **)error;

@end

@implementation KeyWordsDetectionWrapper

- (instancetype)initWithInstanceId:(NSString *)instanceId
                         modelName:(NSString *)modelName
                         threshold:(float)threshold
                         bufferCnt:(NSInteger)bufferCnt
                            bridge:(KeyWordRNBridge *)bridge
                             error:(NSError **)error
{
    if (self = [super init]) {
        _instanceId = instanceId;
        _bridge = bridge;
        _keyWordsDetection = [[KeyWordsDetection alloc] initWithModelPath:modelName threshold:threshold bufferCnt:bufferCnt error:error];
        if (*error) {
            return nil;
        }
        _keyWordsDetection.delegate = self;
    }
    return self;
}

- (instancetype)initWithInstanceId:(NSString *)instanceId
                        modelNames:(NSArray<NSString *> *)modelNames
                        thresholds:(NSArray<NSNumber *> *)thresholds
                        bufferCnts:(NSArray<NSNumber *> *)bufferCnts
                 msBetweenCallback:(NSArray<NSNumber *> *)msBetweenCallback
                            bridge:(KeyWordRNBridge *)bridge
                             error:(NSError **)error {
    if (self = [super init]) {
        _instanceId = instanceId;
        _bridge = bridge;

        NSMutableArray<NSNumber *> *floatThresholds = [NSMutableArray array];
        for (NSNumber *num in thresholds) {
            [floatThresholds addObject:@(num.floatValue)];
        }

        _keyWordsDetection = [[KeyWordsDetection alloc] initWithModelPaths:modelNames
                                                                thresholds:floatThresholds
                                                                bufferCnts:bufferCnts
                                                         msBetweenCallback:msBetweenCallback
                                                                      error:error];
        if (*error) return nil;
        _keyWordsDetection.delegate = self;
    }
    return self;
}

// Implement the delegate method
- (void)KeywordDetectionDidDetectEvent:(NSDictionary *)eventInfo {
    NSMutableDictionary *mutableEventInfo = [eventInfo mutableCopy];
    mutableEventInfo[@"instanceId"] = self.instanceId;
    [_bridge sendEventWithName:@"onKeywordDetectionEvent" body:mutableEventInfo];
}

@end

@interface KeyWordRNBridge () <RCTBridgeModule>

@property (nonatomic, strong) NSMutableDictionary *instances;

@end

@implementation KeyWordRNBridge

RCT_EXPORT_MODULE();

- (instancetype)init {
    if (self = [super init]) {
        _instances = [NSMutableDictionary new];
    }
    return self;
}

+ (BOOL)requiresMainQueueSetup
{
    return YES;
}

- (NSArray<NSString *> *)supportedEvents {
    return @[@"onKeywordDetectionEvent",
             @"onVADDetectionEvent"];  // NEW
}

RCT_EXPORT_METHOD(createInstanceMulti:(NSString *)instanceId
                  modelPaths:(NSArray<NSString *> *)modelPaths
                  thresholds:(NSArray<NSNumber *> *)thresholds
                  bufferCnts:(NSArray<NSNumber *> *)bufferCnts
                  msBetweenCallback:(NSArray<NSNumber *> *)msBetweenCallback
                  resolver:(RCTPromiseResolveBlock)resolve
                  rejecter:(RCTPromiseRejectBlock)reject) {
    if (self.instances[instanceId]) {
        reject(@"InstanceExists", [NSString stringWithFormat:@"Instance already exists with ID: %@", instanceId], nil);
        return;
    }

    NSError *error = nil;
    KeyWordsDetectionWrapper *wrapper = [[KeyWordsDetectionWrapper alloc]
        initWithInstanceId:instanceId
               modelNames:modelPaths
               thresholds:thresholds
               bufferCnts:bufferCnts
        msBetweenCallback:msBetweenCallback
                   bridge:self
                    error:&error];
    if (error) {
        reject(@"CreateError", [NSString stringWithFormat:@"Failed to create multi-model instance: %@", error.localizedDescription], nil);
    } else {
        self.instances[instanceId] = wrapper;
        resolve([NSString stringWithFormat:@"Multi-model instance created with ID: %@", instanceId]);
    }
}

RCT_EXPORT_METHOD(createInstance:(NSString *)instanceId modelName:(NSString *)modelName threshold:(float)threshold bufferCnt:(NSInteger)bufferCnt resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
{
    if (self.instances[instanceId]) {
        reject(@"InstanceExists", [NSString stringWithFormat:@"Instance already exists with ID: %@", instanceId], nil);
        return;
    }

    NSError *error = nil;
    KeyWordsDetectionWrapper *wrapper = [[KeyWordsDetectionWrapper alloc] initWithInstanceId:instanceId modelName:modelName threshold:threshold bufferCnt:bufferCnt bridge:self error:&error];
    if (error) {
        reject(@"CreateError", [NSString stringWithFormat:@"Failed to create instance: %@", error.localizedDescription], nil);
    } else {
        self.instances[instanceId] = wrapper;
        resolve([NSString stringWithFormat:@"Instance created with ID: %@", instanceId]);
    }
}

RCT_EXPORT_METHOD(disableDucking:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
{
  [AudioSessionAndDuckingManager.shared disableDucking];
  resolve(@"enabled");
}

RCT_EXPORT_METHOD(initAudioSessAndDuckManage:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
{
  [AudioSessionAndDuckingManager.shared initAudioSessAndDuckManage];
  resolve(@"enabled");
}

RCT_EXPORT_METHOD(restartListeningAfterDucking:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
{
  [AudioSessionAndDuckingManager.shared restartListeningAfterDucking];
  resolve(@"disabled");
}

RCT_EXPORT_METHOD(enableAggressiveDucking:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
{
  [AudioSessionAndDuckingManager.shared enableAggressiveDucking];
  resolve(@"enabled");
}

RCT_EXPORT_METHOD(disableDuckingAndCleanup:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
{
  [AudioSessionAndDuckingManager.shared disableDuckingAndCleanup];
  resolve(@"disabled");
}

RCT_EXPORT_METHOD(setKeywordDetectionLicense:(NSString *)instanceId licenseKey:(NSString *)licenseKey resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
{
    KeyWordsDetectionWrapper *wrapper = self.instances[instanceId];
    KeyWordsDetection *instance = wrapper.keyWordsDetection;
    BOOL isLicensed = NO;
    if (instance) {
        isLicensed = [instance setLicenseWithLicenseKey:licenseKey];
        NSLog(@"License is valid?: %@", isLicensed ? @"YES" : @"NO");
        resolve(@(isLicensed)); // Wrap BOOL in NSNumber
    } else {
        reject(@"InstanceNotFound", [NSString stringWithFormat:@"No instance found with ID: %@", instanceId], nil);
    }
}

RCT_EXPORT_METHOD(replaceKeywordDetectionModel:(NSString *)instanceId modelName:(NSString *)modelName threshold:(float)threshold bufferCnt:(NSInteger)bufferCnt resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
{
    KeyWordsDetectionWrapper *wrapper = self.instances[instanceId];
    KeyWordsDetection *instance = wrapper.keyWordsDetection;
    if (instance) {
        NSError *error = nil;
        [instance replaceKeywordDetectionModelWithModelPath:modelName threshold:threshold bufferCnt:bufferCnt error:&error];
        if (error) {
            reject(@"ReplaceError", [NSString stringWithFormat:@"Failed to replace model: %@", error.localizedDescription], nil);
        } else {
            resolve([NSString stringWithFormat:@"Instance ID: %@ changed model to %@", instanceId, modelName]);
        }
    } else {
        reject(@"InstanceNotFound", [NSString stringWithFormat:@"No instance found with ID: %@", instanceId], nil);
    }
}

RCT_EXPORT_METHOD(startKeywordDetection:(NSString *)instanceId 
    threshold:(float)threshold 
    setActive:(BOOL)setActive
    duckOthers:(BOOL)duckOthers
    mixWithOthers:(BOOL)mixWithOthers
    defaultToSpeaker:(BOOL)defaultToSpeaker
    resolver:(RCTPromiseResolveBlock)resolve 
    rejecter:(RCTPromiseRejectBlock)reject)
{
    KeyWordsDetectionWrapper *wrapper = self.instances[instanceId];
    KeyWordsDetection *instance = wrapper.keyWordsDetection;
    if (instance) {
        BOOL success = [instance startListeningWithSetActive:setActive
                                                  duckOthers:duckOthers
                                              mixWithOthers:mixWithOthers
                                           defaultToSpeaker:defaultToSpeaker];
        if (success == false) {
            reject(@"StartError", [NSString stringWithFormat:@"Failed to start detection"], nil);
        } else {
            resolve([NSString stringWithFormat:@"Started detection for instance: %@", instanceId]);
        }
    } else {
        reject(@"InstanceNotFound", [NSString stringWithFormat:@"No instance found with ID: %@", instanceId], nil);
    }
}

RCT_EXPORT_METHOD(stopKeywordDetection:(NSString *)instanceId resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
{
    KeyWordsDetectionWrapper *wrapper = self.instances[instanceId];
    KeyWordsDetection *instance = wrapper.keyWordsDetection;
    if (instance) {
        [instance stopListening];
        resolve([NSString stringWithFormat:@"Stopped detection for instance: %@", instanceId]);
    } else {
        reject(@"InstanceNotFound", [NSString stringWithFormat:@"No instance found with ID: %@", instanceId], nil);
    }
}

RCT_EXPORT_METHOD(destroyInstance:(NSString *)instanceId resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
{
    KeyWordsDetectionWrapper *wrapper = self.instances[instanceId];
    if (wrapper) {
        [wrapper.keyWordsDetection stopListening];
        [self.instances removeObjectForKey:instanceId];
        resolve([NSString stringWithFormat:@"Destroyed instance: %@", instanceId]);
    } else {
        reject(@"InstanceNotFound", [NSString stringWithFormat:@"No instance found with ID: %@", instanceId], nil);
    }
}

// Keeping all APIs even if not called in JS yet

RCT_EXPORT_METHOD(getKeywordDetectionModel:(NSString *)instanceId resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
{
    KeyWordsDetectionWrapper *wrapper = self.instances[instanceId];
    KeyWordsDetection *instance = wrapper.keyWordsDetection;
    if (instance) {
        NSString *modelName = [instance getKeywordDetectionModel];
        resolve(modelName);
    } else {
        reject(@"InstanceNotFound", [NSString stringWithFormat:@"No instance found with ID: %@", instanceId], nil);
    }
}

RCT_EXPORT_METHOD(getRecordingWav:(NSString *)instanceId resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
{
    KeyWordsDetectionWrapper *wrapper = self.instances[instanceId];
    KeyWordsDetection *instance = wrapper.keyWordsDetection;
    if (instance) {
        NSString *recWavPath = [instance getRecordingWav];
        resolve(recWavPath);
    } else {
        reject(@"InstanceNotFound", [NSString stringWithFormat:@"No instance found with ID: %@", instanceId], nil);
    }
}

RCT_EXPORT_METHOD(getVoiceProps:(NSString *)instanceId resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
{
    KeyWordsDetectionWrapper *wrapper = self.instances[instanceId];
    KeyWordsDetection *instance = wrapper.keyWordsDetection;
    if (instance) {
        @try {
            NSDictionary *voiceProps = [instance getVoiceProps];
            NSMutableDictionary *result = [NSMutableDictionary dictionary];
            result[@"error"] = voiceProps[@"error"] ?: @"No Error";
            result[@"voiceProbability"] = @([voiceProps[@"voiceProbability"] floatValue]);
            result[@"lastTimeHumanVoiceHeard"] = @([voiceProps[@"lastTimeHumanVoiceHeard"] longLongValue]);
            resolve(result);
        } @catch (NSException *exception) {
            reject(@"VoicePropsError", [NSString stringWithFormat:@"Failed to get voice properties: %@", exception.reason], nil);
        }
    } else {
        reject(@"InstanceNotFound", [NSString stringWithFormat:@"No instance found with ID: %@", instanceId], nil);
    }
}

// Start/stop silent VAD (iOS only)
RCT_EXPORT_METHOD(startSilentVADDetection:(NSString *)instanceId resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
{
    KeyWordsDetectionWrapper *wrapper = self.instances[instanceId];
    if (wrapper && wrapper.keyWordsDetection) {
        BOOL success = [wrapper.keyWordsDetection startSilentListening];
        success ? resolve(@"Started silent VAD detection") :
                  reject(@"StartError", @"Failed to start silent VAD detection", nil);
    } else {
        reject(@"InstanceNotFound", @"No instance found", nil);
    }
}

RCT_EXPORT_METHOD(stopSilentVADDetection:(NSString *)instanceId resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
{
    KeyWordsDetectionWrapper *wrapper = self.instances[instanceId];
    if (wrapper && wrapper.keyWordsDetection) {
        [wrapper.keyWordsDetection stopSilentListening];
        resolve(@"Stopped silent VAD detection");
    } else {
        reject(@"InstanceNotFound", @"No instance found", nil);
    }
}

// Start/stop explicit VAD
RCT_EXPORT_METHOD(startVADDetection:(NSString *)instanceId resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
{
    KeyWordsDetectionWrapper *wrapper = self.instances[instanceId];
    if (wrapper && wrapper.keyWordsDetection) {
        BOOL success = [wrapper.keyWordsDetection startVADListening];
        success ? resolve(@"Started VAD detection") :
                  reject(@"StartError", @"Failed to start VAD detection", nil);
    } else {
        reject(@"InstanceNotFound", @"No instance found", nil);
    }
}

RCT_EXPORT_METHOD(stopVADDetection:(NSString *)instanceId resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
{
    KeyWordsDetectionWrapper *wrapper = self.instances[instanceId];
    if (wrapper && wrapper.keyWordsDetection) {
        [wrapper.keyWordsDetection stopVADListening];
        resolve(@"Stopped VAD detection");
    } else {
        reject(@"InstanceNotFound", @"No instance found", nil);
    }
}

RCT_EXPORT_METHOD(setVADParams:(NSString *)instanceId
                  threshold:(float)threshold
                  msWindow:(NSInteger)msWindow
                  resolver:(RCTPromiseResolveBlock)resolve
                  rejecter:(RCTPromiseRejectBlock)reject)
{
  KeyWordsDetectionWrapper *wrapper = self.instances[instanceId];
  if (wrapper && wrapper.keyWordsDetection) {
    NSError *err = nil;
    BOOL ok = [wrapper.keyWordsDetection setVADParamsWithThreshold:threshold
                                                          msWindow:msWindow
                                                             error:&err];
    if (!ok || err) {
      reject(@"VADParamsError",
             err ? err.localizedDescription : @"Failed to set VAD params",
             err);
    } else {
      resolve(@"VAD params updated");
    }
  } else {
    reject(@"InstanceNotFound", @"No instance found", nil);
  }
}

// You can add more methods here as needed, ensuring they use the instanceId

@end
