/*
 * Copyright 2018 Google
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#import <TargetConditionals.h>
#if TARGET_OS_IOS || TARGET_OS_TV || TARGET_OS_VISION

#import "FirebaseCore/Extension/FirebaseCoreInternal.h"

#import "FirebaseInAppMessaging/Sources/Analytics/FIRIAMClearcutLogStorage.h"
#import "FirebaseInAppMessaging/Sources/FIRCore+InAppMessaging.h"
#import "FirebaseInAppMessaging/Sources/Private/Analytics/FIRIAMClearcutLogger.h"
#import "FirebaseInAppMessaging/Sources/Private/Analytics/FIRIAMClearcutUploader.h"

@interface FIRIAMClearcutLogger ()

// these two writable for assisting unit testing need
@property(readwrite, nonatomic) FIRIAMClearcutHttpRequestSender *requestSender;
@property(readwrite, nonatomic) id<FIRIAMTimeFetcher> timeFetcher;

@property(readonly, nonatomic) FIRIAMClientInfoFetcher *clientInfoFetcher;
@property(readonly, nonatomic) FIRIAMClearcutUploader *ctUploader;

@property(readonly, copy, nonatomic) NSString *fbProjectNumber;
@property(readonly, copy, nonatomic) NSString *fbAppId;

@end

@implementation FIRIAMClearcutLogger {
  // Firebase installations ID.
  NSString *_FID;
}
- (instancetype)initWithFBProjectNumber:(NSString *)fbProjectNumber
                                fbAppId:(NSString *)fbAppId
                      clientInfoFetcher:(FIRIAMClientInfoFetcher *)clientInfoFetcher
                       usingTimeFetcher:(id<FIRIAMTimeFetcher>)timeFetcher
                          usingUploader:(FIRIAMClearcutUploader *)uploader {
  if (self = [super init]) {
    _fbProjectNumber = fbProjectNumber;
    _fbAppId = fbAppId;
    _clientInfoFetcher = clientInfoFetcher;
    _timeFetcher = timeFetcher;
    _ctUploader = uploader;
  }
  return self;
}

+ (void)updateSourceExtensionDictWithAnalyticsEventEnumType:(FIRIAMAnalyticsLogEventType)eventType
                                                    forDict:(NSMutableDictionary *)dict {
  switch (eventType) {
    case FIRIAMAnalyticsEventMessageImpression:
      dict[@"event_type"] = @"IMPRESSION_EVENT_TYPE";
      break;
    case FIRIAMAnalyticsEventActionURLFollow:
      dict[@"event_type"] = @"CLICK_EVENT_TYPE";
      break;
    case FIRIAMAnalyticsEventMessageDismissAuto:
      dict[@"dismiss_type"] = @"AUTO";
      break;
    case FIRIAMAnalyticsEventMessageDismissClick:
      dict[@"dismiss_type"] = @"CLICK";
      break;
    case FIRIAMAnalyticsEventMessageDismissSwipe:
      dict[@"dismiss_type"] = @"SWIPE";
      break;
    case FIRIAMAnalyticsEventImageFetchError:
      dict[@"render_error_reason"] = @"IMAGE_FETCH_ERROR";
      break;
    case FIRIAMAnalyticsEventImageFormatUnsupported:
      dict[@"render_error_reason"] = @"IMAGE_UNSUPPORTED_FORMAT";
      break;
    case FIRIAMAnalyticsEventFetchAPIClientError:
      dict[@"fetch_error_reason"] = @"CLIENT_ERROR";
      break;
    case FIRIAMAnalyticsEventFetchAPIServerError:
      dict[@"fetch_error_reason"] = @"SERVER_ERROR";
      break;
    case FIRIAMAnalyticsEventFetchAPINetworkError:
      dict[@"fetch_error_reason"] = @"NETWORK_ERROR";
      break;
    case FIRIAMAnalyticsEventTestMessageImpression:
      dict[@"event_type"] = @"TEST_MESSAGE_IMPRESSION_EVENT_TYPE";
      break;
    case FIRIAMAnalyticsEventTestMessageClick:
      dict[@"event_type"] = @"TEST_MESSAGE_CLICK_EVENT_TYPE";
      break;
    case FIRIAMAnalyticsLogEventUnknown:
      break;
  }
}

// constructing CampaignAnalytics proto defined in campaign_analytics.proto and serialize it into
// a string.
// @return nil if error happened
- (NSString *)constructSourceExtensionJsonForClearcutWithEventType:
                  (FIRIAMAnalyticsLogEventType)eventType
                                                     forCampaignID:(NSString *)campaignID
                                                     eventTimeInMs:(NSNumber *)eventTimeInMs
                                                    installationID:(NSString *)installationID {
  NSMutableDictionary *campaignAnalyticsDict = [[NSMutableDictionary alloc] init];

  campaignAnalyticsDict[@"project_number"] = self.fbProjectNumber;
  campaignAnalyticsDict[@"campaign_id"] = campaignID;
  campaignAnalyticsDict[@"client_app"] =
      @{@"google_app_id" : self.fbAppId, @"firebase_instance_id" : installationID};
  campaignAnalyticsDict[@"client_timestamp_millis"] = eventTimeInMs;
  [self.class updateSourceExtensionDictWithAnalyticsEventEnumType:eventType
                                                          forDict:campaignAnalyticsDict];

  campaignAnalyticsDict[@"fiam_sdk_version"] = [self.clientInfoFetcher getIAMSDKVersion];

  // turn campaignAnalyticsDict into a json string
  NSError *error;
  NSData *jsonData = [NSJSONSerialization
      dataWithJSONObject:campaignAnalyticsDict  // Here you can pass array or dictionary
                 options:0  // Pass 0 if you don't care about the readability of the generated
                            // string
                   error:&error];

  if (jsonData) {
    NSString *jsonString;
    jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
    FIRLogDebug(kFIRLoggerInAppMessaging, @"I-IAM210006",
                @"Source extension json string produced as %@", jsonString);
    return jsonString;
  } else {
    FIRLogWarning(kFIRLoggerInAppMessaging, @"I-IAM210007",
                  @"Error in generating source extension json string: %@", error);
    return nil;
  }
}

- (void)logAnalyticsEventForType:(FIRIAMAnalyticsLogEventType)eventType
                   forCampaignID:(NSString *)campaignID
               withEventTimeInMs:(nullable NSNumber *)eventTimeInMs
                             FID:(NSString *)FID
                      completion:(void (^)(BOOL success))completion {
  NSTimeInterval nowInMs = [self.timeFetcher currentTimestampInSeconds] * 1000;
  if (eventTimeInMs == nil) {
    eventTimeInMs = @((long)nowInMs);
  }

  if (!FID) {
    FIRLogWarning(kFIRLoggerInAppMessaging, @"I-IAM210009",
                  @"Instance ID is nil, event %ld for campaign ID %@ will not be sent",
                  (long)eventType, campaignID);
    return;
  }

  NSString *sourceExtensionJsonString =
      [self constructSourceExtensionJsonForClearcutWithEventType:eventType
                                                   forCampaignID:campaignID
                                                   eventTimeInMs:eventTimeInMs
                                                  installationID:FID];

  FIRIAMClearcutLogRecord *newRecord = [[FIRIAMClearcutLogRecord alloc]
      initWithExtensionJsonString:sourceExtensionJsonString
          eventTimestampInSeconds:eventTimeInMs.integerValue / 1000];
  [self.ctUploader addNewLogRecord:newRecord];
  FIRLogDebug(kFIRLoggerInAppMessaging, @"I-IAM210003",
              @"One more clearcut log record created and sent to uploader with source extension %@",
              sourceExtensionJsonString);
  completion(YES);
}

- (void)logAnalyticsEventForType:(FIRIAMAnalyticsLogEventType)eventType
                   forCampaignID:(NSString *)campaignID
                withCampaignName:(NSString *)campaignName
                   eventTimeInMs:(nullable NSNumber *)eventTimeInMs
                      completion:(void (^)(BOOL success))completion {
  if (!_FID) {
    [self.clientInfoFetcher
        fetchFirebaseInstallationDataWithProjectNumber:self.fbProjectNumber
                                        withCompletion:^(NSString *_Nullable FID,
                                                         NSString *_Nullable FISToken,
                                                         NSError *_Nullable error) {
                                          if (error) {
                                            FIRLogWarning(
                                                kFIRLoggerInAppMessaging, @"I-IAM210001",
                                                @"Failed to get iid value for clearcut logging %@",
                                                error);
                                            completion(NO);
                                          } else {
                                            // persist FID through the whole life-cycle
                                            self->_FID = FID;
                                            [self logAnalyticsEventForType:eventType
                                                             forCampaignID:campaignID
                                                         withEventTimeInMs:eventTimeInMs
                                                                       FID:FID
                                                                completion:completion];
                                          }
                                        }];
  } else {
    FIRLogDebug(kFIRLoggerInAppMessaging, @"I-IAM210004",
                @"Using remembered iid for event logging");
    [self logAnalyticsEventForType:eventType
                     forCampaignID:campaignID
                 withEventTimeInMs:eventTimeInMs
                               FID:_FID
                        completion:completion];
  }
}
@end

#endif  // TARGET_OS_IOS || TARGET_OS_TV || TARGET_OS_VISION
