#import "ReactNativeDatadome.h"
#import <React/RCTLog.h>
#import <sys/utsname.h>
#import <UIKit/UIKit.h>

@implementation ReactNativeDatadome

RCT_EXPORT_MODULE()

/// MARK: - Cookies

/// MARK: Set DataDome Cookie
- (void) setDataDomeCookie: (NSString *)url cookie: (NSString *)cookie {
  RCTLogInfo(@"Native: Setting cookie %@", cookie);
  NSURL* requestURL = [NSURL URLWithString: url];
  NSHTTPCookieStorage *cookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
   // Set the new DataDome cookie(s)
  NSArray<NSHTTPCookie*>* forgedCookies = [NSHTTPCookie cookiesWithResponseHeaderFields: @{@"Set-Cookie": cookie} forURL: requestURL];

  NSHTTPCookie *newCookie = nil;
  for (int i = 0; i < forgedCookies.count; i++) {
    if ([forgedCookies[i].name isEqualToString:@"datadome"]) {
      newCookie = forgedCookies[i];
      [cookieStorage setCookie:forgedCookies[i]];
    }
  }

  // Clear duplicated DataDome cookies for this domain (only if we set a datadome cookie)
  if (newCookie != nil) {
    NSArray<NSHTTPCookie *> *existingCookies = [cookieStorage cookiesForURL:requestURL];

    // Use compound predicate for better debugging and compile-time checking
    NSPredicate *predicateOnName = [NSPredicate predicateWithFormat:@"name == %@", @"datadome"];
    NSPredicate *predicateOnValue = [NSPredicate predicateWithFormat:@"value != %@", newCookie.value];
    NSPredicate *predicate = [NSCompoundPredicate andPredicateWithSubpredicates:@[predicateOnName, predicateOnValue]];

    NSArray *duplicatedDatadomeCookies = [existingCookies filteredArrayUsingPredicate:predicate];
    for (NSHTTPCookie *duplicatedDatadomeCookie in duplicatedDatadomeCookies) {
      RCTLogInfo(@"Native: Deleting duplicated DataDome cookie for domain %@", duplicatedDatadomeCookie.domain);
      [cookieStorage deleteCookie:duplicatedDatadomeCookie];
    }
  }
}

RCT_REMAP_METHOD(setDataDomeCookie, setCookieForURL: (NSString *)url
                 cookie: (NSString *)cookie
                 withResolver:(RCTPromiseResolveBlock) resolve
                 withRejecter:(RCTPromiseRejectBlock) reject) {
    [self setDataDomeCookie: url cookie:cookie];
    resolve(nil);
}

/// MARK: Get DataDome Cookie
- (NSString *) getDataDomeCookie: (NSString *)url {
  NSHTTPCookieStorage *cookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
  NSArray<NSHTTPCookie *> *cookies = [cookieStorage cookiesForURL:[NSURL URLWithString:url]];

  for (NSHTTPCookie *cookie in cookies) {
    if ([cookie.name isEqualToString:@"datadome"]) {
      NSString *cookieString = [NSString stringWithFormat:@"%@=%@", cookie.name, cookie.value];
      return cookieString;
    }
  }

  return @"";
}

RCT_REMAP_METHOD(getDataDomeCookie, getCookie:(NSString *)url
                 withResolver:(RCTPromiseResolveBlock) resolve
                 withRejecter:(RCTPromiseRejectBlock) reject) {
  return resolve([self getDataDomeCookie: url]);
}

/// MARK: - Signals

/// MARK: Application Version
- (NSString *) appVersion {
  NSString *version = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleShortVersionString"];
  return version ? version : @"1.0";
}

RCT_REMAP_METHOD(appVersion, appVersionWithResolver: (RCTPromiseResolveBlock) resolve
                  withRejecter: (RCTPromiseRejectBlock) reject) {
  return resolve([self appVersion]);
}

/// MARK: System Version
- (NSString *) systemVersion {
  return [[UIDevice currentDevice] systemVersion];
}

RCT_REMAP_METHOD(systemVersion, systemVersionWithResolver:(RCTPromiseResolveBlock) resolve
                 withRejecter:(RCTPromiseRejectBlock) reject) {
  return resolve([self systemVersion]);
}

/// MARK: System Name
- (NSString *) systemName {
  return [[UIDevice currentDevice] systemName];
}

RCT_REMAP_METHOD(systemName, systemNameWithResolver:(RCTPromiseResolveBlock) resolve
                 withRejecter:(RCTPromiseRejectBlock) reject) {
  return resolve([self systemName]);
}

/// MARK: System Short Version
- (NSString *) systemShortVersion {
  CGFloat version = (([[UIDevice currentDevice] systemVersion].floatValue * 10.0) / 10.0);
  NSString *versionString = [NSString stringWithFormat:@"%.1f", version];
  return versionString;
}

RCT_REMAP_METHOD(systemShortVersion, systemShortVersionWithResolver:(RCTPromiseResolveBlock) resolve
                 withRejecter:(RCTPromiseRejectBlock) reject) {
  return resolve([self systemShortVersion]);
}

/// MARK: Device Model
- (NSString *) deviceModel {
  struct utsname systemInfo;
  uname(&systemInfo);

  NSString *model = [NSString stringWithCString:systemInfo.machine encoding:NSUTF8StringEncoding];
  return model;
}

RCT_REMAP_METHOD(deviceModel, deviceModelWithResolver:(RCTPromiseResolveBlock) resolve
                 withRejecter:(RCTPromiseRejectBlock) reject) {
  return resolve([self deviceModel]);
}

/// MARK: Device Screen Width
- (NSString *) deviceScreenWidth {
  NSString *widthString = [NSString stringWithFormat:@"%.1f", UIScreen.mainScreen.nativeBounds.size.width];
  return widthString;
}

RCT_REMAP_METHOD(deviceScreenWidth, deviceScreenWidthWithResolver:(RCTPromiseResolveBlock) resolve
                 withRejecter:(RCTPromiseRejectBlock) reject) {
  return resolve([self deviceScreenWidth]);
}

/// MARK: Device Screen Height
- (NSString *) deviceScreenHeight {
  NSString *heightString = [NSString stringWithFormat:@"%.1f", UIScreen.mainScreen.nativeBounds.size.height];
  return heightString;
}

RCT_REMAP_METHOD(deviceScreenHeight, deviceScreenHeightWithResolver:(RCTPromiseResolveBlock) resolve
                 withRejecter:(RCTPromiseRejectBlock) reject) {
  return resolve([self deviceScreenHeight]);
}

/// MARK: Device Screen Scale
- (NSString *) deviceScreenScale {
  NSString *scaleString = [NSString stringWithFormat:@"%.1f", UIScreen.mainScreen.nativeScale];
  return scaleString;
}

RCT_REMAP_METHOD(deviceScreenScale, deviceScreenScaleWithResolver:(RCTPromiseResolveBlock) resolve
                 withRejecter:(RCTPromiseRejectBlock) reject) {
  return resolve([self deviceScreenScale]);
}

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

@end
