#import "Clarity.h"
#import <React/RCTLog.h>

@import Clarity;

@implementation Clarity

RCT_EXPORT_MODULE()

RCT_EXPORT_METHOD(initialize:(NSString *)projectId
                  userId:(NSString *)userId
                  logLevel:(NSString *)logLevel
                  resolver:(RCTPromiseResolveBlock)resolve
                  rejecter:(RCTPromiseRejectBlock)reject)
{
    dispatch_async(dispatch_get_main_queue(), ^{
        ClarityConfig* config = [[ClarityConfig alloc] initWithProjectId:projectId];

        config.userId = [self sanitizeBridgedValue:userId];
        config.logLevel = [self convertLogLevelToEnum:logLevel];
        config.applicationFramework = ClarityApplicationFrameworkReactNative;

        resolve(@([ClaritySDK initializeWithConfig:config]));
    });
}

RCT_EXPORT_METHOD(pause:(RCTPromiseResolveBlock)resolve
                  rejecter:(RCTPromiseRejectBlock)reject)
{
    dispatch_async(dispatch_get_main_queue(), ^{
        [ClaritySDK pause];
        resolve(@([ClaritySDK isPaused]));
    });
}

RCT_EXPORT_METHOD(resume:(RCTPromiseResolveBlock)resolve
                  rejecter:(RCTPromiseRejectBlock)reject)
{
    dispatch_async(dispatch_get_main_queue(), ^{
        [ClaritySDK resume];
        resolve(@((BOOL)(![ClaritySDK isPaused])));
    });
}

RCT_EXPORT_METHOD(isPaused:(RCTPromiseResolveBlock)resolve
                  rejecter:(RCTPromiseRejectBlock)reject)
{
    resolve(@([ClaritySDK isPaused]));
}

RCT_EXPORT_METHOD(startNewSession:(RCTResponseSenderBlock)callback)
{
    dispatch_async(dispatch_get_main_queue(), ^{
        [ClaritySDK startNewSessionWithCallback:^(NSString *sessionId) {
            callback(@[sessionId]);
        }];
    });
}

RCT_EXPORT_METHOD(setCustomUserId:(NSString *)customUserId
                  resolver:(RCTPromiseResolveBlock)resolve
                  rejecter:(RCTPromiseRejectBlock)reject)
{
    dispatch_async(dispatch_get_main_queue(), ^{
        resolve(@([ClaritySDK setCustomUserId:[self sanitizeBridgedValue:customUserId]]));
    });
}

RCT_EXPORT_METHOD(setCustomSessionId:(NSString *)customSessionId
                  resolver:(RCTPromiseResolveBlock)resolve
                  rejecter:(RCTPromiseRejectBlock)reject)
{
    dispatch_async(dispatch_get_main_queue(), ^{
        resolve(@([ClaritySDK setCustomSessionId:[self sanitizeBridgedValue:customSessionId]]));
    });
}

RCT_EXPORT_METHOD(getCurrentSessionId:(RCTPromiseResolveBlock)resolve
                  rejecter:(RCTPromiseRejectBlock)reject)
{
    resolve([ClaritySDK getCurrentSessionId]);
}

RCT_EXPORT_METHOD(getCurrentSessionUrl:(RCTPromiseResolveBlock)resolve
                  rejecter:(RCTPromiseRejectBlock)reject)
{
    resolve([ClaritySDK getCurrentSessionUrl]);
}

RCT_EXPORT_METHOD(setCustomTag:(NSString *)key value:(NSString *)value
                  resolver:(RCTPromiseResolveBlock)resolve
                  rejecter:(RCTPromiseRejectBlock)reject)
{
    resolve(@([ClaritySDK setCustomTagWithKey:[self sanitizeBridgedValue:key]
                                        value:[self sanitizeBridgedValue:value]]));
}

RCT_EXPORT_METHOD(setCustomTags:(NSString *)key
                  values:(NSArray<NSString *> *)values
                  resolver:(RCTPromiseResolveBlock)resolve
                  rejecter:(RCTPromiseRejectBlock)reject)
{
    NSArray<NSString *> *sanitizedValues = [self sanitizeBridgedStringArray:values];
    NSSet<NSString *> *valueSet = sanitizedValues.count > 0 ? [NSSet setWithArray:sanitizedValues] : nil;

    resolve(@([ClaritySDK setCustomTagWithKey:[self sanitizeBridgedValue:key]
                                       values:valueSet]));
}

RCT_EXPORT_METHOD(sendCustomEvent:(NSString *)value
                  resolver:(RCTPromiseResolveBlock)resolve
                  rejecter:(RCTPromiseRejectBlock)reject)
{
    resolve(@([ClaritySDK sendCustomEventWithValue:[self sanitizeBridgedValue:value]]));
}

RCT_EXPORT_METHOD(setCurrentScreenName:(NSString *)currentScreenName
                  resolver:(RCTPromiseResolveBlock)resolve
                  rejecter:(RCTPromiseRejectBlock)reject)
{
    dispatch_async(dispatch_get_main_queue(), ^{
        resolve(@([ClaritySDK setCurrentScreenName:[self sanitizeBridgedValue:currentScreenName]]));
    });
}

RCT_EXPORT_METHOD(consent:(BOOL)adsStorage
                  analyticsStorage:(BOOL)analyticsStorage
                  resolver:(RCTPromiseResolveBlock)resolve
                  rejecter:(RCTPromiseRejectBlock)reject)
{
    dispatch_async(dispatch_get_main_queue(), ^{
        resolve(@([ClaritySDK consentWithAnalyticsStorage:analyticsStorage]));
    });
}

- (ClarityLogLevel)convertLogLevelToEnum:(NSString*)logLevel
{
    if ([logLevel isEqual:@"Verbose"]) {
        return ClarityLogLevelVerbose;
    }

    if ([logLevel isEqual:@"Debug"]) {
        return ClarityLogLevelDebug;
    }

    if ([logLevel isEqual:@"Info"]) {
        return ClarityLogLevelInfo;
    }

    if ([logLevel isEqual:@"Warning"]) {
        return ClarityLogLevelWarning;
    }

    if ([logLevel isEqual:@"Error"]) {
        return ClarityLogLevelError;
    }

    return ClarityLogLevelNone;
}

/**
 * Ensures compatibility between React Native and the Swift-based Clarity modules.
 *
 * While newer versions of React Native automatically convert bridged values
 * to valid Objective-C types, `NSNull` remains incompatible with Clarity's
 * Swift-based interface. This method safely translates `NSNull` to `nil`
 * to prevent unexpected crashes.
 *
 * @param value The bridged value from React Native, which may be `NSNull`.
 * @return The original value if valid, or `nil` if the value is `NSNull`.
 */
- (id)sanitizeBridgedValue:(id)value {
    if ([value isKindOfClass:[NSNull class]]) {
        return nil;
    }

    return value;
}

- (NSArray<NSString *> *)sanitizeBridgedStringArray:(id)values {
    if (![values isKindOfClass:[NSArray class]]) {
        return @[];
    }

    NSMutableArray<NSString *> *sanitized = [NSMutableArray array];

    for (id value in (NSArray *)values) {
        id sanitizedValue = [self sanitizeBridgedValue:value];

        if (sanitizedValue == nil) {
            continue;
        }

        if ([sanitizedValue isKindOfClass:[NSString class]]) {
            [sanitized addObject:sanitizedValue];
        }
    }

    return sanitized;
}

@end
