// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#import "ios/chrome/browser/metrics/model/incognito_usage_app_state_agent.h"

#import "base/metrics/histogram_functions.h"
#import "base/time/time.h"
#import "ios/chrome/app/application_delegate/app_state.h"
#import "ios/chrome/browser/shared/coordinator/scene/scene_state.h"
#import "ios/chrome/browser/shared/coordinator/scene/state/incognito_state.h"

namespace {
// Minimum amount of time for a normal/incognito transition to be considered.
constexpr base::TimeDelta kMinimumDelay = base::Seconds(10);
}  // namespace

@interface IncognitoUsageAppStateAgent () <IncognitoStateObserver>

@property(nonatomic, assign) BOOL incognitoContentVisible;

@property(nonatomic, assign) base::TimeTicks incognitoUsageStart;
@property(nonatomic, assign) base::TimeTicks incognitoUsageEnd;

@end

@implementation IncognitoUsageAppStateAgent

- (instancetype)init {
  self = [super init];
  if (self) {
    [NSNotificationCenter.defaultCenter
        addObserver:self
           selector:@selector(applicationWillTerminate)
               name:UIApplicationWillTerminateNotification
             object:nil];
  }
  return self;
}

- (void)setAppState:(AppState*)appState {
  [super setAppState:appState];
  for (SceneState* scene in self.appState.connectedScenes) {
    [scene.incognitoState addObserver:self];
  }
}

#pragma mark - Private

- (BOOL)checkIncognitoContentVisible {
  for (SceneState* scene in self.appState.connectedScenes) {
    if (scene.incognitoState.incognitoContentVisible &&
        scene.activationLevel >= SceneActivationLevelForegroundInactive) {
      return YES;
    }
  }
  return NO;
}

- (void)reportIncognitoUsageTime {
  DCHECK(!self.incognitoUsageStart.is_null());
  DCHECK(!self.incognitoUsageEnd.is_null());
  base::TimeDelta duration = self.incognitoUsageEnd - self.incognitoUsageStart;
  if (duration < kMinimumDelay) {
    return;
  }
  base::UmaHistogramCustomTimes("IOS.Incognito.TimeSpent", duration,
                                base::Seconds(1),
                                base::Seconds(86400 /* secs per day */), 50);
  self.incognitoUsageStart = base::TimeTicks();
  self.incognitoUsageEnd = base::TimeTicks();
}

- (void)updateIncognitoContentVisible {
  BOOL incognitoContentVisible = [self checkIncognitoContentVisible];
  if (self.incognitoContentVisible == incognitoContentVisible) {
    return;
  }

  self.incognitoContentVisible = incognitoContentVisible;
  if (incognitoContentVisible) {
    base::TimeTicks now = base::TimeTicks::Now();
    if (!self.incognitoUsageEnd.is_null() &&
        (now - self.incognitoUsageEnd) < kMinimumDelay) {
      // The pausing of incognito is too short, resume session.
      self.incognitoUsageEnd = base::TimeTicks();
    } else {
      // Incognito has been paused for a long time. This is a new session.
      if (!self.incognitoUsageEnd.is_null() &&
          !self.incognitoUsageStart.is_null() &&
          (self.incognitoUsageEnd - self.incognitoUsageStart) >=
              kMinimumDelay) {
        // There was a previous session to report.
        [self reportIncognitoUsageTime];
      }
      self.incognitoUsageStart = base::TimeTicks::Now();
    }
  } else {
    base::TimeTicks now = base::TimeTicks::Now();
    if (!self.incognitoUsageStart.is_null() &&
        (now - self.incognitoUsageStart) >= kMinimumDelay) {
      self.incognitoUsageEnd = now;
    } else {
      // This incognito session was too short.
      self.incognitoUsageStart = base::TimeTicks();
    }
  }
}

- (void)applicationWillTerminate {
  if (self.incognitoContentVisible) {
    self.incognitoUsageEnd = base::TimeTicks::Now();
  }
  if (!self.incognitoUsageEnd.is_null() &&
      !self.incognitoUsageStart.is_null() &&
      (self.incognitoUsageEnd - self.incognitoUsageStart) >= kMinimumDelay) {
    [self reportIncognitoUsageTime];
  }
}

#pragma mark - SceneStateObserver

- (void)appState:(AppState*)appState sceneConnected:(SceneState*)sceneState {
  [super appState:appState sceneConnected:sceneState];
  [sceneState.incognitoState addObserver:self];
}

- (void)sceneState:(SceneState*)sceneState
    transitionedToActivationLevel:(SceneActivationLevel)level {
  [super sceneState:sceneState transitionedToActivationLevel:level];
  if (sceneState.incognitoState.incognitoContentVisible) {
    [self updateIncognitoContentVisible];
  }
}

#pragma mark - IncognitoStateObserver

- (void)willEnterIncognitoForState:(IncognitoState*)incognitoState {
  if (incognitoState.sceneState.activationLevel >=
      SceneActivationLevelForegroundInactive) {
    [self updateIncognitoContentVisible];
  }
}

- (void)willExitIncognitoForState:(IncognitoState*)incognitoState {
  if (incognitoState.sceneState.activationLevel >=
      SceneActivationLevelForegroundInactive) {
    [self updateIncognitoContentVisible];
  }
}

@end
