// Copyright © 2022 Olo Inc. All rights reserved.
// This software is made available under the Olo Pay SDK License (See LICENSE.md file)

#import "PaymentCardCvvViewComponentView.h"
#import <React/RCTConversions.h>
#import <React/RCTFabricComponentsPlugins.h>
#import <react/renderer/components/OlopaysdkReactNativeSpec/ComponentDescriptors.h>
#import <react/renderer/components/OlopaysdkReactNativeSpec/EventEmitters.h>
#import <react/renderer/components/OlopaysdkReactNativeSpec/Props.h>
#import <react/renderer/components/OlopaysdkReactNativeSpec/RCTComponentViewHelpers.h>
#import "OloPaySDKForwardDeclarations.h"
#import "olopaysdk_react_native-Swift.h"

using namespace facebook::react;

@interface PaymentCardCvvViewComponentView () <RCTPaymentCardCvvViewViewProtocol>
@end

@implementation PaymentCardCvvViewComponentView {
    PaymentCardCvvView *_swiftView;
}

- (instancetype)initWithFrame:(CGRect)frame
{
    if (self = [super initWithFrame:frame]) {
        static const auto defaultProps = std::make_shared<const PaymentCardCvvViewProps>();
        _props = defaultProps;

        _swiftView = [[PaymentCardCvvView alloc] initWithFrame:self.bounds];
        _swiftView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;

        __weak __typeof__(self) weakSelf = self;
        _swiftView.onCvvChange = ^(NSDictionary *data) {
            [weakSelf sendCvvChangeEvent:data];
        };
        _swiftView.onFocus = ^(NSDictionary *data) {
            [weakSelf sendFocusEvent:data];
        };
        _swiftView.onBlur = ^(NSDictionary *data) {
            [weakSelf sendBlurEvent:data];
        };
        _swiftView.onCvvTokenResult = ^(NSDictionary *data) {
            [weakSelf sendCvvTokenResultEvent:data];
        };

        self.contentView = _swiftView;
    }
    return self;
}

#pragma mark - RCTComponentViewProtocol

+ (ComponentDescriptorProvider)componentDescriptorProvider
{
    return concreteComponentDescriptorProvider<PaymentCardCvvViewComponentDescriptor>();
}

- (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps
{
    const auto &oldViewProps = *std::static_pointer_cast<PaymentCardCvvViewProps const>(_props);
    const auto &newViewProps = *std::static_pointer_cast<PaymentCardCvvViewProps const>(props);

    if (oldViewProps.isEnabled != newViewProps.isEnabled) {
        _swiftView.isEnabled = newViewProps.isEnabled;
    }

    if (oldViewProps.placeholder != newViewProps.placeholder) {
        _swiftView.placeholder = RCTNSStringFromString(newViewProps.placeholder);
    }

    // Update CVV styles - always update (struct comparison not supported)
    {
        NSMutableDictionary *styles = [NSMutableDictionary dictionary];

        if (!newViewProps.cvvStyles.backgroundColor.empty()) {
            styles[@"backgroundColor"] = RCTNSStringFromString(newViewProps.cvvStyles.backgroundColor);
        }
        if (!newViewProps.cvvStyles.borderColor.empty()) {
            styles[@"borderColor"] = RCTNSStringFromString(newViewProps.cvvStyles.borderColor);
        }
        if (newViewProps.cvvStyles.borderWidth > 0) {
            styles[@"borderWidth"] = @(newViewProps.cvvStyles.borderWidth);
        }
        if (newViewProps.cvvStyles.cornerRadius > 0) {
            styles[@"cornerRadius"] = @(newViewProps.cvvStyles.cornerRadius);
        }
        if (!newViewProps.cvvStyles.cursorColor.empty()) {
            styles[@"cursorColor"] = RCTNSStringFromString(newViewProps.cvvStyles.cursorColor);
        }
        if (!newViewProps.cvvStyles.fontFamily.empty()) {
            styles[@"fontFamily"] = RCTNSStringFromString(newViewProps.cvvStyles.fontFamily);
        }
        if (newViewProps.cvvStyles.fontSize > 0) {
            styles[@"fontSize"] = @(newViewProps.cvvStyles.fontSize);
        }
        if (!newViewProps.cvvStyles.placeholderColor.empty()) {
            styles[@"placeholderColor"] = RCTNSStringFromString(newViewProps.cvvStyles.placeholderColor);
        }
        if (!newViewProps.cvvStyles.textColor.empty()) {
            styles[@"textColor"] = RCTNSStringFromString(newViewProps.cvvStyles.textColor);
        }
        if (!newViewProps.cvvStyles.errorTextColor.empty()) {
            styles[@"errorTextColor"] = RCTNSStringFromString(newViewProps.cvvStyles.errorTextColor);
        }
        if (newViewProps.cvvStyles.textPaddingLeft > 0) {
            styles[@"textPaddingLeft"] = @(newViewProps.cvvStyles.textPaddingLeft);
        }
        if (newViewProps.cvvStyles.textPaddingRight > 0) {
            styles[@"textPaddingRight"] = @(newViewProps.cvvStyles.textPaddingRight);
        }
        if (!newViewProps.cvvStyles.fontWeight.empty()) {
            styles[@"fontWeight"] = RCTNSStringFromString(newViewProps.cvvStyles.fontWeight);
        }
        // italic is a bool, always pass it
        styles[@"italic"] = @(newViewProps.cvvStyles.italic);
        if (!newViewProps.cvvStyles.textAlign.empty()) {
            styles[@"textAlign"] = RCTNSStringFromString(newViewProps.cvvStyles.textAlign);
        }

        [_swiftView updateCvvStyles:styles];
    }

    // Update custom error messages - always update (struct comparison not supported)
    {
        NSMutableDictionary *errorMessages = [NSMutableDictionary dictionary];

        if (!newViewProps.customErrorMessages.invalidError.empty()) {
            errorMessages[@"invalidError"] = RCTNSStringFromString(newViewProps.customErrorMessages.invalidError);
        }
        if (!newViewProps.customErrorMessages.emptyError.empty()) {
            errorMessages[@"emptyError"] = RCTNSStringFromString(newViewProps.customErrorMessages.emptyError);
        }

        [_swiftView updateCustomErrorMessages:errorMessages];
    }

    [super updateProps:props oldProps:oldProps];
}

#pragma mark - Commands

- (void)handleCommand:(const NSString *)commandName args:(const NSArray *)args
{
    RCTPaymentCardCvvViewHandleCommand(self, commandName, args);
}

- (void)focus
{
    [_swiftView focus];
}

- (void)blur
{
    [_swiftView blur];
}

- (void)clear
{
    [_swiftView clear];
}

- (void)createCvvUpdateToken
{
    [_swiftView createCvvUpdateToken];
}

- (void)prepareForRecycle
{
    // Calling super first nulls the _eventEmitter so clear doesn't send events
    // as the view is being unmounted and put into the recycle pool
    [super prepareForRecycle];
    [_swiftView clear];
}

#pragma mark - Event Emitters

- (void)sendCvvChangeEvent:(NSDictionary *)data
{
    if (_eventEmitter) {
        auto viewEventEmitter = std::static_pointer_cast<PaymentCardCvvViewEventEmitter const>(_eventEmitter);

        PaymentCardCvvViewEventEmitter::OnCvvChangeEvent event;

        NSDictionary *stateDict = data[@"state"];
        PaymentCardCvvViewEventEmitter::OnCvvChangeEventState state;
        state.isValid = [stateDict[@"isValid"] boolValue];
        state.isFocused = [stateDict[@"isFocused"] boolValue];
        state.isEmpty = [stateDict[@"isEmpty"] boolValue];
        state.wasEdited = [stateDict[@"wasEdited"] boolValue];
        state.wasFocused = [stateDict[@"wasFocused"] boolValue];
        event.state = state;

        // Add errors if present
        if (data[@"errors"]) {
            PaymentCardCvvViewEventEmitter::OnCvvChangeEventErrors errors;
            if (data[@"errors"][@"uneditedFieldError"]) {
                errors.uneditedFieldError = std::string([data[@"errors"][@"uneditedFieldError"] UTF8String]);
            }
            if (data[@"errors"][@"editedFieldError"]) {
                errors.editedFieldError = std::string([data[@"errors"][@"editedFieldError"] UTF8String]);
            }
            event.errors = errors;
        }

        viewEventEmitter->onCvvChangeEvent(event);
    }
}

- (void)sendFocusEvent:(NSDictionary *)data
{
    if (_eventEmitter) {
        auto viewEventEmitter = std::static_pointer_cast<PaymentCardCvvViewEventEmitter const>(_eventEmitter);

        PaymentCardCvvViewEventEmitter::OnFocusEvent event;

        NSDictionary *stateDict = data[@"state"];
        PaymentCardCvvViewEventEmitter::OnFocusEventState state;
        state.isValid = [stateDict[@"isValid"] boolValue];
        state.isFocused = [stateDict[@"isFocused"] boolValue];
        state.isEmpty = [stateDict[@"isEmpty"] boolValue];
        state.wasEdited = [stateDict[@"wasEdited"] boolValue];
        state.wasFocused = [stateDict[@"wasFocused"] boolValue];
        event.state = state;

        // Add errors if present
        if (data[@"errors"]) {
            PaymentCardCvvViewEventEmitter::OnFocusEventErrors errors;
            if (data[@"errors"][@"uneditedFieldError"]) {
                errors.uneditedFieldError = std::string([data[@"errors"][@"uneditedFieldError"] UTF8String]);
            }
            if (data[@"errors"][@"editedFieldError"]) {
                errors.editedFieldError = std::string([data[@"errors"][@"editedFieldError"] UTF8String]);
            }
            event.errors = errors;
        }

        viewEventEmitter->onFocusEvent(event);
    }
}

- (void)sendBlurEvent:(NSDictionary *)data
{
    if (_eventEmitter) {
        auto viewEventEmitter = std::static_pointer_cast<PaymentCardCvvViewEventEmitter const>(_eventEmitter);

        PaymentCardCvvViewEventEmitter::OnBlurEvent event;

        NSDictionary *stateDict = data[@"state"];
        PaymentCardCvvViewEventEmitter::OnBlurEventState state;
        state.isValid = [stateDict[@"isValid"] boolValue];
        state.isFocused = [stateDict[@"isFocused"] boolValue];
        state.isEmpty = [stateDict[@"isEmpty"] boolValue];
        state.wasEdited = [stateDict[@"wasEdited"] boolValue];
        state.wasFocused = [stateDict[@"wasFocused"] boolValue];
        event.state = state;

        // Add errors if present
        if (data[@"errors"]) {
            PaymentCardCvvViewEventEmitter::OnBlurEventErrors errors;
            if (data[@"errors"][@"uneditedFieldError"]) {
                errors.uneditedFieldError = std::string([data[@"errors"][@"uneditedFieldError"] UTF8String]);
            }
            if (data[@"errors"][@"editedFieldError"]) {
                errors.editedFieldError = std::string([data[@"errors"][@"editedFieldError"] UTF8String]);
            }
            event.errors = errors;
        }

        viewEventEmitter->onBlurEvent(event);
    }
}

- (void)sendCvvTokenResultEvent:(NSDictionary *)data
{
    if (_eventEmitter) {
        auto viewEventEmitter = std::static_pointer_cast<PaymentCardCvvViewEventEmitter const>(_eventEmitter);

        PaymentCardCvvViewEventEmitter::OnCvvTokenResultEvent event;

        // Handle token (success case)
        if (data[@"token"]) {
            NSDictionary *tokenData = data[@"token"];
            PaymentCardCvvViewEventEmitter::OnCvvTokenResultEventToken token;

            token.id = std::string([tokenData[@"id"] UTF8String] ?: "");
            token.productionEnvironment = [tokenData[@"productionEnvironment"] boolValue];

            event.token = token;
        }

        // Handle error case
        if (data[@"error"]) {
            NSDictionary *errorData = data[@"error"];
            PaymentCardCvvViewEventEmitter::OnCvvTokenResultEventError error;

            NSString *codeStr = errorData[@"code"];
            NSString *messageStr = errorData[@"message"];
            error.code = codeStr ? std::string([codeStr UTF8String]) : "";
            error.message = messageStr ? std::string([messageStr UTF8String]) : "";

            event.error = error;
        }

        viewEventEmitter->onCvvTokenResultEvent(event);
    }
}

@end

Class<RCTComponentViewProtocol> PaymentCardCvvViewCls(void)
{
    return PaymentCardCvvViewComponentView.class;
}
