#import <React/RCTBridgeModule.h>
#import <React/RCTLog.h>
#import <GoogleSignIn/GoogleSignIn.h>
#import "RCTConvert+PortalGoogleSignIn.h"

#ifdef RCT_NEW_ARCH_ENABLED
#import <React/RCTBridge+Private.h>
#import <React/RCTUtils.h>
#import <ReactCommon/RCTTurboModule.h>
#import "PortalGoogleSignInSpec.h"
#endif

@interface RCT_EXTERN_MODULE(PortalGoogleSignIn, NSObject)

// MARK: - Old Architecture Methods

#ifndef RCT_NEW_ARCH_ENABLED

RCT_EXTERN_METHOD(configure:(NSDictionary *)options
                  resolve:(RCTPromiseResolveBlock)resolve
                  reject:(RCTPromiseRejectBlock)reject)

RCT_EXTERN_METHOD(signIn:(NSDictionary *)options
                  resolve:(RCTPromiseResolveBlock)resolve
                  reject:(RCTPromiseRejectBlock)reject)

RCT_EXTERN_METHOD(addScopes:(NSDictionary *)options
                  resolve:(RCTPromiseResolveBlock)resolve
                  reject:(RCTPromiseRejectBlock)reject)

#endif

// MARK: - Common Methods (both archs)

RCT_EXTERN_METHOD(signInSilently:(RCTPromiseResolveBlock)resolve
                  reject:(RCTPromiseRejectBlock)reject)

RCT_EXTERN_METHOD(signOut:(RCTPromiseResolveBlock)resolve
                  reject:(RCTPromiseRejectBlock)reject)

RCT_EXTERN_METHOD(revokeAccess:(RCTPromiseResolveBlock)resolve
                  reject:(RCTPromiseRejectBlock)reject)

RCT_EXTERN_METHOD(isSignedIn:(RCTPromiseResolveBlock)resolve
                  reject:(RCTPromiseRejectBlock)reject)

RCT_EXTERN_METHOD(getCurrentUser:(RCTPromiseResolveBlock)resolve
                  reject:(RCTPromiseRejectBlock)reject)

RCT_EXTERN_METHOD(getTokens:(RCTPromiseResolveBlock)resolve
                  reject:(RCTPromiseRejectBlock)reject)

RCT_EXTERN_METHOD(clearCachedAccessToken:(NSString *)tokenString
                  resolve:(RCTPromiseResolveBlock)resolve
                  reject:(RCTPromiseRejectBlock)reject)

RCT_EXTERN_METHOD(playServicesAvailable:(BOOL)showDialog
                  resolve:(RCTPromiseResolveBlock)resolve
                  reject:(RCTPromiseRejectBlock)reject)

@end

// MARK: - Module configuration & constants

@implementation PortalGoogleSignIn (BridgeConfig)

+ (BOOL)requiresMainQueueSetup {
    return NO;
}

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

- (NSDictionary *)constantsToExport {
    return @{
        @"BUTTON_SIZE_ICON": @(PortalGIDSignInButtonStyleIconOnly),
        @"BUTTON_SIZE_STANDARD": @(PortalGIDSignInButtonStyleStandard),
        @"BUTTON_SIZE_WIDE": @(PortalGIDSignInButtonStyleWide),
        @"BUTTON_COLOR_LIGHT": @(PortalGIDSignInButtonColorSchemeLight),
        @"BUTTON_COLOR_DARK": @(PortalGIDSignInButtonColorSchemeDark),
        @"SIGN_IN_CANCELLED": [NSString stringWithFormat:@"%ld", (long)kGIDSignInErrorCodeCanceled],
        @"SIGN_IN_REQUIRED": [NSString stringWithFormat:@"%ld", (long)kGIDSignInErrorCodeHasNoAuthInKeychain],
        @"IN_PROGRESS": @"ASYNC_OP_IN_PROGRESS",
        @"PLAY_SERVICES_NOT_AVAILABLE": @"PLAY_SERVICES_NOT_AVAILABLE"
    };
}

#ifdef RCT_NEW_ARCH_ENABLED
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:
    (const facebook::react::ObjCTurboModule::InitParams &)params {
    return std::make_shared<facebook::react::NativePortalGoogleSignInSpecJSI>(params);
}
#endif

@end

// MARK: - New Architecture: TurboModule C++ methods
// These methods accept C++ struct types from codegen and convert to NSDictionary
// before calling the Swift implementation methods.

#ifdef RCT_NEW_ARCH_ENABLED

@protocol PortalGoogleSignInImpl <NSObject>
- (void)configureImpl:(NSDictionary *)options resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject;
- (void)signInImpl:(NSDictionary *)options resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject;
- (void)addScopesImpl:(NSDictionary *)options resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject;
@end

@interface PortalGoogleSignIn (TurboModule) <NativePortalGoogleSignInSpec>
@end

@implementation PortalGoogleSignIn (TurboModule)

- (void)configure:(JS::NativePortalGoogleSignIn::GoogleSignInConfig &)config
          resolve:(RCTPromiseResolveBlock)resolve
           reject:(RCTPromiseRejectBlock)reject {
    NSMutableDictionary *options = [NSMutableDictionary dictionary];
    if (config.iosClientId()) {
        options[@"iosClientId"] = config.iosClientId();
    }
    if (config.webClientId()) {
        options[@"webClientId"] = config.webClientId();
    }
    if (config.hostedDomain()) {
        options[@"hostedDomain"] = config.hostedDomain();
    }
    if (config.openIDRealm()) {
        options[@"openIDRealm"] = config.openIDRealm();
    }
    if (config.googleServicePlistPath()) {
        options[@"googleServicePlistPath"] = config.googleServicePlistPath();
    }
    if (config.profileImageSize().has_value()) {
        options[@"profileImageSize"] = @(static_cast<NSUInteger>(config.profileImageSize().value()));
    }
    if (config.scopes().has_value()) {
        auto scopesVec = config.scopes().value();
        NSMutableArray *scopesArray = [NSMutableArray array];
        for (NSString *scope : scopesVec) {
            [scopesArray addObject:scope];
        }
        options[@"scopes"] = scopesArray;
    }
    [(id<PortalGoogleSignInImpl>)self configureImpl:options resolve:resolve reject:reject];
}

- (void)signIn:(JS::NativePortalGoogleSignIn::GoogleSignInOptions &)options
       resolve:(RCTPromiseResolveBlock)resolve
        reject:(RCTPromiseRejectBlock)reject {
    NSMutableDictionary *dict = [NSMutableDictionary dictionary];
    if (options.loginHint()) {
        dict[@"loginHint"] = options.loginHint();
    }
    [(id<PortalGoogleSignInImpl>)self signInImpl:dict resolve:resolve reject:reject];
}

- (void)addScopes:(JS::NativePortalGoogleSignIn::AddScopesOptions &)options
          resolve:(RCTPromiseResolveBlock)resolve
           reject:(RCTPromiseRejectBlock)reject {
    NSMutableDictionary *dict = [NSMutableDictionary dictionary];
    auto scopesVec = options.scopes();
    NSMutableArray *scopes = [NSMutableArray array];
    for (NSString *scope : scopesVec) {
        [scopes addObject:scope];
    }
    dict[@"scopes"] = scopes;
    [(id<PortalGoogleSignInImpl>)self addScopesImpl:dict resolve:resolve reject:reject];
}

- (facebook::react::ModuleConstants<JS::NativePortalGoogleSignIn::Constants>)getConstants {
    return facebook::react::typedConstants<JS::NativePortalGoogleSignIn::Constants>({
        .BUTTON_SIZE_ICON = static_cast<double>(PortalGIDSignInButtonStyleIconOnly),
        .BUTTON_SIZE_STANDARD = static_cast<double>(PortalGIDSignInButtonStyleStandard),
        .BUTTON_SIZE_WIDE = static_cast<double>(PortalGIDSignInButtonStyleWide),
        .BUTTON_COLOR_LIGHT = static_cast<double>(PortalGIDSignInButtonColorSchemeLight),
        .BUTTON_COLOR_DARK = static_cast<double>(PortalGIDSignInButtonColorSchemeDark),
        .SIGN_IN_CANCELLED = [NSString stringWithFormat:@"%ld", (long)kGIDSignInErrorCodeCanceled],
        .SIGN_IN_REQUIRED = [NSString stringWithFormat:@"%ld", (long)kGIDSignInErrorCodeHasNoAuthInKeychain],
        .IN_PROGRESS = @"ASYNC_OP_IN_PROGRESS",
        .PLAY_SERVICES_NOT_AVAILABLE = @"PLAY_SERVICES_NOT_AVAILABLE"
    });
}

@end

#endif
