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

#import "PaymentCardDetailsFormComponentView.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 PaymentCardDetailsFormComponentView () <RCTPaymentCardDetailsFormViewProtocol>
@end

@implementation PaymentCardDetailsFormComponentView {
    PaymentCardDetailsForm *_swiftView;
}

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

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

        __weak __typeof__(self) weakSelf = self;
        _swiftView.onFormInputChange = ^(NSDictionary *data) {
            [weakSelf sendFormCompleteEvent:data];
        };
        _swiftView.onPaymentMethodResult = ^(NSDictionary *data) {
            [weakSelf sendPaymentMethodResultEvent:data];
        };

        self.contentView = _swiftView;
    }
    return self;
}

#pragma mark - RCTComponentViewProtocol

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

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

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

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

        if (!newViewProps.cardStyles.backgroundColor.empty()) {
            styles[@"backgroundColor"] = RCTNSStringFromString(newViewProps.cardStyles.backgroundColor);
        }
        if (!newViewProps.cardStyles.borderColor.empty()) {
            styles[@"borderColor"] = RCTNSStringFromString(newViewProps.cardStyles.borderColor);
        }
        if (newViewProps.cardStyles.borderWidth > 0) {
            styles[@"borderWidth"] = @(newViewProps.cardStyles.borderWidth);
        }
        if (newViewProps.cardStyles.cornerRadius > 0) {
            styles[@"cornerRadius"] = @(newViewProps.cardStyles.cornerRadius);
        }
        if (!newViewProps.cardStyles.cursorColor.empty()) {
            styles[@"cursorColor"] = RCTNSStringFromString(newViewProps.cardStyles.cursorColor);
        }
        if (!newViewProps.cardStyles.fontFamily.empty()) {
            styles[@"fontFamily"] = RCTNSStringFromString(newViewProps.cardStyles.fontFamily);
        }
        if (newViewProps.cardStyles.fontSize > 0) {
            styles[@"fontSize"] = @(newViewProps.cardStyles.fontSize);
        }
        if (!newViewProps.cardStyles.placeholderColor.empty()) {
            styles[@"placeholderColor"] = RCTNSStringFromString(newViewProps.cardStyles.placeholderColor);
        }
        if (!newViewProps.cardStyles.textColor.empty()) {
            styles[@"textColor"] = RCTNSStringFromString(newViewProps.cardStyles.textColor);
        }
        if (newViewProps.cardStyles.textPaddingLeft > 0) {
            styles[@"textPaddingLeft"] = @(newViewProps.cardStyles.textPaddingLeft);
        }
        if (newViewProps.cardStyles.textPaddingRight > 0) {
            styles[@"textPaddingRight"] = @(newViewProps.cardStyles.textPaddingRight);
        }
        if (!newViewProps.cardStyles.fontWeight.empty()) {
            styles[@"fontWeight"] = RCTNSStringFromString(newViewProps.cardStyles.fontWeight);
        }
        // italic is a bool, always pass it
        styles[@"italic"] = @(newViewProps.cardStyles.italic);
        // Form-specific styles
        if (!newViewProps.cardStyles.fieldDividerColor.empty()) {
            styles[@"fieldDividerColor"] = RCTNSStringFromString(newViewProps.cardStyles.fieldDividerColor);
        }
        if (newViewProps.cardStyles.fieldDividerWidth > 0) {
            styles[@"fieldDividerWidth"] = @(newViewProps.cardStyles.fieldDividerWidth);
        }
        if (!newViewProps.cardStyles.focusedPlaceholderColor.empty()) {
            styles[@"focusedPlaceholderColor"] = RCTNSStringFromString(newViewProps.cardStyles.focusedPlaceholderColor);
        }
        if (newViewProps.cardStyles.cardElevation > 0) {
            styles[@"cardElevation"] = @(newViewProps.cardStyles.cardElevation);
        }

        [_swiftView updateFieldStyles:styles];
    }

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

        if (!newViewProps.placeholders.number.empty()) {
            placeholders[@"number"] = RCTNSStringFromString(newViewProps.placeholders.number);
        }
        if (!newViewProps.placeholders.expiration.empty()) {
            placeholders[@"expiration"] = RCTNSStringFromString(newViewProps.placeholders.expiration);
        }
        if (!newViewProps.placeholders.cvv.empty()) {
            placeholders[@"cvv"] = RCTNSStringFromString(newViewProps.placeholders.cvv);
        }
        if (!newViewProps.placeholders.postalCode.empty()) {
            placeholders[@"postalCode"] = RCTNSStringFromString(newViewProps.placeholders.postalCode);
        }

        [_swiftView updatePlaceholders:placeholders];
    }

    [super updateProps:props oldProps:oldProps];
}

#pragma mark - Commands

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

- (void)focus:(NSString *)field
{
    [_swiftView focusField:field];
}

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

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

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

- (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)sendFormCompleteEvent:(NSDictionary *)data
{
    if (_eventEmitter) {
        auto viewEventEmitter = std::static_pointer_cast<PaymentCardDetailsFormEventEmitter const>(_eventEmitter);

        PaymentCardDetailsFormEventEmitter::OnFormCompleteEvent event = {
            .isValid = [data[@"isValid"] boolValue]
        };

        viewEventEmitter->onFormCompleteEvent(event);
    }
}

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

        PaymentCardDetailsFormEventEmitter::OnPaymentMethodResultEvent event;

        // Handle paymentMethod (success case)
        if (data[@"paymentMethod"]) {
            NSDictionary *paymentMethodData = data[@"paymentMethod"];
            PaymentCardDetailsFormEventEmitter::OnPaymentMethodResultEventPaymentMethod paymentMethod;

            paymentMethod.id = std::string([paymentMethodData[@"id"] UTF8String] ?: "");
            paymentMethod.last4 = std::string([paymentMethodData[@"last4"] UTF8String] ?: "");
            paymentMethod.cardType = std::string([paymentMethodData[@"cardType"] UTF8String] ?: "");
            paymentMethod.expMonth = [paymentMethodData[@"expMonth"] intValue];
            paymentMethod.expYear = [paymentMethodData[@"expYear"] intValue];
            paymentMethod.postalCode = std::string([paymentMethodData[@"postalCode"] UTF8String] ?: "");
            paymentMethod.countryCode = std::string([paymentMethodData[@"countryCode"] UTF8String] ?: "");
            paymentMethod.isDigitalWallet = [paymentMethodData[@"isDigitalWallet"] boolValue];
            paymentMethod.productionEnvironment = [paymentMethodData[@"productionEnvironment"] boolValue];
            paymentMethod.email = std::string([paymentMethodData[@"email"] UTF8String] ?: "");
            paymentMethod.digitalWalletCardDescription = std::string([paymentMethodData[@"digitalWalletCardDescription"] UTF8String] ?: "");
            paymentMethod.fullName = std::string([paymentMethodData[@"fullName"] UTF8String] ?: "");
            paymentMethod.fullPhoneticName = std::string([paymentMethodData[@"fullPhoneticName"] UTF8String] ?: "");
            paymentMethod.phoneNumber = std::string([paymentMethodData[@"phoneNumber"] UTF8String] ?: "");

            // Handle billing address
            NSDictionary *billingAddressData = paymentMethodData[@"billingAddress"];
            PaymentCardDetailsFormEventEmitter::OnPaymentMethodResultEventPaymentMethodBillingAddress billingAddress;
            billingAddress.address1 = std::string([billingAddressData[@"address1"] UTF8String] ?: "");
            billingAddress.address2 = std::string([billingAddressData[@"address2"] UTF8String] ?: "");
            billingAddress.address3 = std::string([billingAddressData[@"address3"] UTF8String] ?: "");
            billingAddress.locality = std::string([billingAddressData[@"locality"] UTF8String] ?: "");
            billingAddress.postalCode = std::string([billingAddressData[@"postalCode"] UTF8String] ?: "");
            billingAddress.countryCode = std::string([billingAddressData[@"countryCode"] UTF8String] ?: "");
            billingAddress.administrativeArea = std::string([billingAddressData[@"administrativeArea"] UTF8String] ?: "");
            billingAddress.sortingCode = std::string([billingAddressData[@"sortingCode"] UTF8String] ?: "");
            paymentMethod.billingAddress = billingAddress;

            event.paymentMethod = paymentMethod;
        }

        // Handle error case
        if (data[@"error"]) {
            NSDictionary *errorData = data[@"error"];
            PaymentCardDetailsFormEventEmitter::OnPaymentMethodResultEventError 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->onPaymentMethodResultEvent(event);
    }
}

@end

Class<RCTComponentViewProtocol> PaymentCardDetailsFormCls(void)
{
    return PaymentCardDetailsFormComponentView.class;
}
