#import "UserLeapBindings.h"
#import <React/RCTUtils.h>
#import <React/RCTTextView.h>
#import <React/RCTUIManager.h>
#import <React/RCTTextShadowView.h>
#import <React/RCTShadowView.h>
#import <React/RCTRawTextShadowView.h>
#import <React/RCTVirtualTextShadowView.h>
#import <React/RCTUIManagerUtils.h>
#import <React/RCTView.h>

@implementation UserLeapBindings

@synthesize bridge = _bridge;

- (dispatch_queue_t)methodQueue
{
    return dispatch_get_main_queue();
}

-(NSDictionary *) parseSurveyResult:(SprigSurveyResult *)result {
    SurveyState state = [result surveyState];
    NSString *surveyStateString = [self getSurveyState:state];
    NSInteger surveyId = [result surveyId];
    return @{@"surveyState": surveyStateString, @"surveyId": @(surveyId)};
}

- (NSString *)getSurveyState:(SurveyState)surveyState
{
    NSString *surveyStateBinding = @"NO_SURVEY";
    switch(surveyState) {
        case SurveyStateDisabled:
            surveyStateBinding = @"DISABLED";
            break;
        case SurveyStateReady:
            surveyStateBinding = @"READY";
            break;
        case SurveyStateNoSurvey:
            surveyStateBinding = @"NO_SURVEY";
            break;
        case SurveyStatePreviousSurveyReady:
            surveyStateBinding = @"PREVIOUS_SURVEY_READY";
            break;
    }
    return surveyStateBinding;
}

RCT_EXPORT_MODULE()

RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(visitorIdentifier)
{
    return [[UserLeap shared] visitorIdentifier];
}

RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(visitorIdentifierString)
{
    return [[UserLeap shared] visitorIdentifierString];
}

RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(sdkVersion)
{
    return [[UserLeap shared] sdkVersion];
}

RCT_EXPORT_METHOD(configure:(NSString *)environmentId configuration:(NSDictionary *)configuration )
{
    [[UserLeap shared] configureWithEnvironment:environmentId configuration:configuration];
    [[UserLeap shared] _passWithRnExtractor:self];
}


RCT_EXPORT_METHOD(setPreviewKey:(NSString *)previewKey)
{
    [[UserLeap shared] setPreviewKey:previewKey];
}

RCT_EXPORT_METHOD(presentSurvey)
{
    [[UserLeap shared] presentSurveyFrom:RCTPresentedViewController()];
}

/// Use trackEvent instead of track
RCT_EXPORT_METHOD(track:(NSString *)eventName
                  surveyStateCallback:(RCTResponseSenderBlock)callback)
{
    if (callback != nil) {
        [[UserLeap shared] trackWithEventName:eventName handler:^(enum SurveyState surveyState) {
            callback(@[[self getSurveyState:surveyState]]);
        }];
    } else {
        [[UserLeap shared] trackWithEventName:eventName handler:nil];
    }
}

RCT_EXPORT_METHOD(trackEvent:(NSString *)eventName
                  surveyResultCallback:(RCTResponseSenderBlock)callback)
{
    EventPayload *payload = [[EventPayload alloc] initWithEventName:eventName userId:nil partnerAnonymousId:nil properties:nil handler:nil resultHandler:^(SprigSurveyResult * result) {
            if (callback != nil) {
                callback(@[[self parseSurveyResult:result]]);
            }
        }];
    [[UserLeap shared] trackWithPayload: payload];
}

/// Use trackEventWithProperties instead of trackWithProperties
RCT_EXPORT_METHOD(trackWithProperties:(NSString *)eventName
                  userId:(NSString * _Nullable)userId
                  partnerAnonymousId:(NSString * _Nullable)partnerAnonymousId
                  properties: (NSDictionary *)properties
                  surveyStateCallback:(RCTResponseSenderBlock)callback)
{
        [[UserLeap shared] trackWithEventName:eventName userId:userId partnerAnonymousId:partnerAnonymousId properties:properties handler:^(enum SurveyState surveyState) {
            callback(@[[self getSurveyState:surveyState]]);
        }];
    
}

RCT_EXPORT_METHOD(trackEventWithProperties:(NSString *)eventName
                  userId:(NSString * _Nullable)userId
                  partnerAnonymousId:(NSString * _Nullable)partnerAnonymousId
                  properties: (NSDictionary *)properties
                  surveyResultCallback:(RCTResponseSenderBlock)callback)
{
    EventPayload *payload = [[EventPayload alloc] initWithEventName:eventName
                                                             userId:userId
                                                 partnerAnonymousId:partnerAnonymousId
                                                         properties:properties
                                                            handler:nil
                                                      resultHandler:^(SprigSurveyResult * result) {
            if (callback != nil) {
                callback(@[[self parseSurveyResult:result]]);
            }
        }];
    [[UserLeap shared] trackWithPayload: payload];
}

/// Use trackEventAndIdentify instead of trackAndIdentify
RCT_EXPORT_METHOD(trackAndIdentify:(NSString *)eventName
                  userId:(NSString *)userId
                  partnerAnonymousId:(NSString *)partnerAnonymousId
                  handler:(RCTResponseSenderBlock)callback)
{
    if (callback != nil) {
        [[UserLeap shared] trackWithEventName:eventName userId:userId partnerAnonymousId:partnerAnonymousId handler:^(enum SurveyState surveyState) {
            callback(@[[self getSurveyState:surveyState]]);
        }];
    } else {
        [[UserLeap shared] trackWithEventName:eventName userId:userId partnerAnonymousId:partnerAnonymousId handler:nil];
    }
}

RCT_EXPORT_METHOD(trackEventAndIdentify:(NSString *)eventName
                  userId:(NSString *)userId
                  partnerAnonymousId:(NSString *)partnerAnonymousId
                  surveyResultCallback:(RCTResponseSenderBlock)callback)
{
    EventPayload *payload = [[EventPayload alloc] initWithEventName:eventName
                                                             userId:userId
                                                 partnerAnonymousId:partnerAnonymousId
                                                         properties:nil
                                                            handler:nil
                                                      resultHandler:^(SprigSurveyResult * result) {
            if (callback != nil) {
                callback(@[[self parseSurveyResult:result]]);
            }
        }];
    [[UserLeap shared] trackWithPayload: payload];
}

RCT_EXPORT_METHOD(setEmailAddress:(NSString *)emailAddress)
{
    [[UserLeap shared] setEmailAddress:emailAddress];
}

RCT_EXPORT_METHOD(setVisitorAttribute:(NSString *)key value: (NSString *)value)
{
    [[UserLeap shared] setVisitorAttributeWithKey:key value:value];
}

RCT_EXPORT_METHOD(setVisitorAttributes:(NSDictionary *)attributes)
{
    [[UserLeap shared] setVisitorAttributes:attributes];
}

RCT_EXPORT_METHOD(setVisitorAttributesAndIdentify:(NSDictionary *)attributes userId:(NSString *)userId partnerAnonymousId:(NSString*)partnerAnonymousId)
{
    [[UserLeap shared] setVisitorAttributes:attributes userId:userId partnerAnonymousId:partnerAnonymousId];
}

RCT_EXPORT_METHOD(removeVisitorAttributes:(NSArray<NSString *> *)attributes)
{
    [[UserLeap shared] removeVisitorAttributes:attributes];
}

RCT_EXPORT_METHOD(setUserIdentifier:(NSString *)identifier)
{
    [[UserLeap shared] setUserIdentifier:identifier];
}

RCT_EXPORT_METHOD(logout)
{
    [[UserLeap shared] logout];
}

RCT_EXPORT_METHOD(trackAndPresent:(NSString *)eventName)
{
    [[UserLeap shared] trackAndPresentWithEventName:eventName from:RCTPresentedViewController()];
}

RCT_EXPORT_METHOD(trackIdentifyAndPresent:(NSString *)eventName userId:(NSString *)userId partnerAnonymousId:(NSString *)partnerAnonymousId)
{
    [[UserLeap shared] trackAndPresentWithEventName:eventName userId:userId partnerAnonymousId:partnerAnonymousId from:RCTPresentedViewController()];
}

- (_SGRNTextProperties *)textPropertiesFromView:(UIView * _Nonnull)passedView {

    // Legacy for pre new arch.
    if ([passedView isKindOfClass:[RCTTextView class]]) { 
        RCTUIManager* uiManager = [self.bridge moduleForClass:[RCTUIManager class]];
        RCTTextView *textView = (RCTTextView *)passedView;
        __block _SGRNTextProperties *textProperties = [[_SGRNTextProperties alloc] init];
        dispatch_sync(RCTGetUIManagerQueue(), ^{
            RCTShadowView *shadowView = [uiManager shadowViewForReactTag:textView.reactTag];
            if ([shadowView isKindOfClass:[RCTTextShadowView class]]) {
                RCTTextShadowView *textShadowView = (RCTTextShadowView *)shadowView;
                NSString *text = [self extractText: textShadowView.reactSubviews];
                if (text.length == 0) return;
                textProperties.text = text;
                textProperties.color = textShadowView.textAttributes.foregroundColor;
                textProperties.alignment = textShadowView.textAttributes.alignment;
                textProperties.font = textShadowView.textAttributes.effectiveFont;
            }
        });
        if (textProperties.text.length == 0) return nil;
        return textProperties;
    }

    // Text under new arch.
    Class ParagraphComponentView = NSClassFromString(@"RCTParagraphComponentView");
    if (ParagraphComponentView && [passedView isKindOfClass:ParagraphComponentView]) {
        NSString *text = [self extractTextFromParagraphComponentView:passedView];
        if (text.length == 0) return nil;
        _SGRNTextProperties *textProperties = [[_SGRNTextProperties alloc] init];
        textProperties.text = text;
        if ([passedView respondsToSelector:@selector(attributedText)]) {
            NSAttributedString *attrText = [passedView performSelector:@selector(attributedText)];
            if (attrText.length > 0) {
                NSDictionary *attrs = [attrText attributesAtIndex:0 effectiveRange:nil];
                if (!textProperties.color && attrs[NSForegroundColorAttributeName]) {
                    textProperties.color = attrs[NSForegroundColorAttributeName];
                }
                if (!textProperties.font && attrs[NSFontAttributeName]) {
                    textProperties.font = attrs[NSFontAttributeName];
                }
                NSParagraphStyle *style = attrs[NSParagraphStyleAttributeName];
                if (style) {
                    textProperties.alignment = style.alignment;
                }
            }
        }
        return textProperties;
    }
    return nil;
}

RCT_EXPORT_METHOD(dismissActiveSurvey)
{
    [[UserLeap shared] dismissActiveSurvey];
}

RCT_EXPORT_METHOD(pauseDisplayingSurveys)
{
    [[UserLeap shared] pauseDisplayingSurveys];
}

RCT_EXPORT_METHOD(unpauseDisplayingSurveys)
{
    [[UserLeap shared] unpauseDisplayingSurveys];
}

RCT_EXPORT_METHOD(overrideUserInterfaceMode:(NSInteger)mode)
{
    [[UserLeap shared] overrideUserInterfaceModeWithMode:mode];
}

- (_SGRNViewProperties * _Nullable)propertiesFromView:(UIView * _Nonnull)passedView {
    if ([passedView isKindOfClass:[RCTView class]]) {
        RCTView *rView = (RCTView *) passedView;
        if (rView.borderWidth > 0) {
            _SGRNViewProperties *props = [[_SGRNViewProperties alloc] init];
            props.borderColor = rView.borderColor;
            props.borderWidth = rView.borderWidth;
            return props;
        }
    }
    return nil;
}

- (NSString * _Nullable)extractText:(NSArray<RCTShadowView *> * _Nonnull) subviews {
    NSMutableString *text = [[NSMutableString alloc] init];
    for (RCTShadowView *subview in subviews) {
        if ([subview isKindOfClass:[RCTRawTextShadowView class]]) {
            [text appendString:((RCTRawTextShadowView *)subview).text];
        }
        if ([subview isKindOfClass:[RCTVirtualTextShadowView class]]) {
            return [self extractText: ((RCTVirtualTextShadowView *)subview).reactSubviews];
        }
    }
    return text;
}

- (NSString *)extractTextFromParagraphComponentView:(UIView *)view {
    if ([view respondsToSelector:@selector(attributedText)]) {
        NSAttributedString *attrText = [view performSelector:@selector(attributedText)];
        return attrText.string;
    }
    if ([view respondsToSelector:@selector(text)]) {
        NSString *text = [view performSelector:@selector(text)];
        return text;
    }
    NSMutableString *result = [NSMutableString string];
    for (UIView *subview in view.subviews) {
        NSString *subText = [self extractTextFromParagraphComponentView:subview];
        if (subText) [result appendString:subText];
    }
    return result;
}

@end
