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

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

@implementation PaymentCardDetailsViewComponentView {
    PaymentCardDetailsView *_swiftView;
}

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

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

        __weak __typeof__(self) weakSelf = self;
        _swiftView.onCardInputChange = ^(NSDictionary *data) {
            [weakSelf sendCardChangeEvent:data];
        };
        _swiftView.onFocus = ^(NSDictionary *data) {
            [weakSelf sendFocusEvent];
        };
        _swiftView.onBlur = ^(NSDictionary *data) {
            [weakSelf sendBlurEvent];
        };
        _swiftView.onFocusField = ^(NSDictionary *data) {
            [weakSelf sendFocusFieldEvent:data];
        };
        _swiftView.onPaymentMethodResult = ^(NSDictionary *data) {
            [weakSelf sendPaymentMethodResultEvent:data];
        };

        self.contentView = _swiftView;
    }
    return self;
}

#pragma mark - RCTComponentViewProtocol

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

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

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

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

    // 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.errorTextColor.empty()) {
            styles[@"errorTextColor"] = RCTNSStringFromString(newViewProps.cardStyles.errorTextColor);
        }
        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);

        [_swiftView updateCardStyles:styles];
    }

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

        // Convert nested C++ struct to nested NSDictionary
        // number field
        NSMutableDictionary *numberErrors = [NSMutableDictionary dictionary];
        if (!newViewProps.customErrorMessages.number.invalidError.empty()) {
            numberErrors[@"invalidError"] = RCTNSStringFromString(newViewProps.customErrorMessages.number.invalidError);
        }
        if (!newViewProps.customErrorMessages.number.emptyError.empty()) {
            numberErrors[@"emptyError"] = RCTNSStringFromString(newViewProps.customErrorMessages.number.emptyError);
        }
        if (numberErrors.count > 0) {
            errorMessages[@"number"] = numberErrors;
        }

        // expiration field
        NSMutableDictionary *expirationErrors = [NSMutableDictionary dictionary];
        if (!newViewProps.customErrorMessages.expiration.invalidError.empty()) {
            expirationErrors[@"invalidError"] = RCTNSStringFromString(newViewProps.customErrorMessages.expiration.invalidError);
        }
        if (!newViewProps.customErrorMessages.expiration.emptyError.empty()) {
            expirationErrors[@"emptyError"] = RCTNSStringFromString(newViewProps.customErrorMessages.expiration.emptyError);
        }
        if (expirationErrors.count > 0) {
            errorMessages[@"expiration"] = expirationErrors;
        }

        // cvv field
        NSMutableDictionary *cvvErrors = [NSMutableDictionary dictionary];
        if (!newViewProps.customErrorMessages.cvv.invalidError.empty()) {
            cvvErrors[@"invalidError"] = RCTNSStringFromString(newViewProps.customErrorMessages.cvv.invalidError);
        }
        if (!newViewProps.customErrorMessages.cvv.emptyError.empty()) {
            cvvErrors[@"emptyError"] = RCTNSStringFromString(newViewProps.customErrorMessages.cvv.emptyError);
        }
        if (cvvErrors.count > 0) {
            errorMessages[@"cvv"] = cvvErrors;
        }

        // postalCode field
        NSMutableDictionary *postalCodeErrors = [NSMutableDictionary dictionary];
        if (!newViewProps.customErrorMessages.postalCode.invalidError.empty()) {
            postalCodeErrors[@"invalidError"] = RCTNSStringFromString(newViewProps.customErrorMessages.postalCode.invalidError);
        }
        if (!newViewProps.customErrorMessages.postalCode.emptyError.empty()) {
            postalCodeErrors[@"emptyError"] = RCTNSStringFromString(newViewProps.customErrorMessages.postalCode.emptyError);
        }
        if (postalCodeErrors.count > 0) {
            errorMessages[@"postalCode"] = postalCodeErrors;
        }

        // unsupportedCardError (top-level string)
        if (!newViewProps.customErrorMessages.unsupportedCardError.empty()) {
            errorMessages[@"unsupportedCardError"] = RCTNSStringFromString(newViewProps.customErrorMessages.unsupportedCardError);
        }

        [_swiftView updateCustomErrorMessages:errorMessages];
    }

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

        // Convert NSArray to std::vector for invalidFields and emptyFields
        std::vector<std::string> invalidFields;
        if (data[@"invalidFields"]) {
            for (NSString *field in data[@"invalidFields"]) {
                invalidFields.push_back(std::string([field UTF8String]));
            }
        }

        std::vector<std::string> emptyFields;
        if (data[@"emptyFields"]) {
            for (NSString *field in data[@"emptyFields"]) {
                emptyFields.push_back(std::string([field UTF8String]));
            }
        }

        PaymentCardDetailsViewEventEmitter::OnCardChangeEvent event = {
            .isValid = [data[@"isValid"] boolValue],
            .cardType = std::string([data[@"cardType"] UTF8String] ?: ""),
            .invalidFields = invalidFields,
            .emptyFields = emptyFields
        };

        // Add errors if present
        if (data[@"errors"]) {
            PaymentCardDetailsViewEventEmitter::OnCardChangeEventErrors errors;
            if (data[@"errors"][@"allFieldsError"]) {
                errors.allFieldsError = std::string([data[@"errors"][@"allFieldsError"] UTF8String]);
            }
            if (data[@"errors"][@"editedFieldsError"]) {
                errors.editedFieldsError = std::string([data[@"errors"][@"editedFieldsError"] UTF8String]);
            }
            event.errors = errors;
        }

        viewEventEmitter->onCardChangeEvent(event);
    }
}

- (void)sendFocusEvent
{
    if (_eventEmitter) {
        auto viewEventEmitter = std::static_pointer_cast<PaymentCardDetailsViewEventEmitter const>(_eventEmitter);
        viewEventEmitter->onFocusEvent({});
    }
}

- (void)sendBlurEvent
{
    if (_eventEmitter) {
        auto viewEventEmitter = std::static_pointer_cast<PaymentCardDetailsViewEventEmitter const>(_eventEmitter);
        viewEventEmitter->onBlurEvent({});
    }
}

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

        PaymentCardDetailsViewEventEmitter::OnFocusFieldEvent event = {
            .field = std::string([data[@"field"] UTF8String] ?: "")
        };

        viewEventEmitter->onFocusFieldEvent(event);
    }
}

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

        PaymentCardDetailsViewEventEmitter::OnPaymentMethodResultEvent event;

        // Handle paymentMethod (success case)
        if (data[@"paymentMethod"]) {
            NSDictionary *paymentMethodData = data[@"paymentMethod"];
            PaymentCardDetailsViewEventEmitter::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"];
            PaymentCardDetailsViewEventEmitter::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"];
            PaymentCardDetailsViewEventEmitter::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> PaymentCardDetailsViewCls(void)
{
    return PaymentCardDetailsViewComponentView.class;
}
