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

#import "base/strings/stringprintf.h"
#import "components/feature_engagement/public/feature_constants.h"
#import "components/omnibox/browser/omnibox_pref_names.h"
#import "ios/chrome/browser/intelligence/features/features.h"
#import "ios/chrome/browser/location_bar/badge/ui/location_bar_badge_constants.h"
#import "ios/chrome/browser/shared/model/prefs/pref_names.h"
#import "ios/chrome/browser/shared/public/features/features.h"
#import "ios/chrome/test/earl_grey/chrome_actions.h"
#import "ios/chrome/test/earl_grey/chrome_earl_grey.h"
#import "ios/chrome/test/earl_grey/chrome_earl_grey_ui.h"
#import "ios/chrome/test/earl_grey/chrome_matchers.h"
#import "ios/chrome/test/earl_grey/chrome_test_case.h"
#import "ios/testing/earl_grey/earl_grey_test.h"
#import "net/test/embedded_test_server/embedded_test_server.h"
#import "net/test/embedded_test_server/http_request.h"
#import "net/test/embedded_test_server/http_response.h"
#import "net/test/embedded_test_server/request_handler_util.h"

namespace {

// The page height of test pages. This must be big enough to triger fullscreen.
const int kPageHeightEM = 200;

// Provides test page long enough to allow fullscreen.
std::unique_ptr<net::test_server::HttpResponse> GetLongResponseForFullscreen(
    const net::test_server::HttpRequest& request) {
  auto result = std::make_unique<net::test_server::BasicHttpResponse>();
  result->set_code(net::HTTP_OK);
  result->set_content(base::StringPrintf(
      "<p style='height:%dem'>test1</p><p>test2</p>", kPageHeightEM));
  return result;
}

// Returns the Contextual Panel's entrypoint view GREY matcher.
id<GREYMatcher> ContextualPanelEntrypointImageViewMatcher() {
  return grey_allOf(grey_accessibilityID(kLocationBarBadgeImageViewIdentifier),
                    grey_interactable(), nil);
}

}  // namespace

@interface ContextualPanelTestCase : ChromeTestCase
@end

@implementation ContextualPanelTestCase

- (void)setUp {
  [super setUp];
  [ChromeEarlGrey
      resetDataForLocalStatePref:omnibox::kIsOmniboxInBottomPosition];

  self.testServer->RegisterRequestHandler(base::BindRepeating(
      &net::test_server::HandlePrefixedRequest, "/long-fullscreen",
      base::BindRepeating(&GetLongResponseForFullscreen)));

  bool started = self.testServer->Start();
  GREYAssertTrue(started, @"Test server failed to start.");
}

- (void)tearDownHelper {
  [super tearDownHelper];
  [ChromeEarlGrey
      resetDataForLocalStatePref:omnibox::kIsOmniboxInBottomPosition];
}

- (AppLaunchConfiguration)appConfigurationForTestCase {
  AppLaunchConfiguration config;

  config.relaunch_policy = ForceRelaunchByCleanShutdown;

  config.features_enabled.push_back(kContextualPanelForceShowEntrypoint);
  config.features_enabled.push_back(kPageActionMenu);

  if ([self isRunningTest:@selector(testOpenContextualPanelFromIPH)] ||
      [self isRunningTest:@selector(testOrientationChangeDismissesIPH)]) {
    config.iph_feature_enabled =
        feature_engagement::kIPHiOSContextualPanelSampleModelFeature.name;
  }

  // TODO(crbug.com/467331873): Re-enable this when the test is updated for
  // PSF.
  config.features_disabled.push_back(kProactiveSuggestionsFramework);
  return config;
}

// TODO(crbug.com/523259471): Fix this flaky test.
// Tests that the contextual panel opens correctly.
- (void)testOpenContextualPanel {
#if TARGET_IPHONE_SIMULATOR
  if (@available(iOS 18, *)) {
    EARL_GREY_TEST_DISABLED(@"Flaky on iOS 18 simulator, crbug.com/523259471");
  }
#endif

  [ChromeEarlGrey loadURL:self.testServer->GetURL("/defaultresponse")];

  [ChromeEarlGrey waitForUIElementToAppearWithMatcher:
                      ContextualPanelEntrypointImageViewMatcher()];

  [[EarlGrey
      selectElementWithMatcher:ContextualPanelEntrypointImageViewMatcher()]
      performAction:grey_tap()];

  // Check that the contextual panel opened up.
  [[EarlGrey
      selectElementWithMatcher:grey_accessibilityID(@"PanelContentViewAXID")]
      assertWithMatcher:grey_sufficientlyVisible()];

  // Close panel
  [[EarlGrey
      selectElementWithMatcher:grey_accessibilityID(@"PanelCloseButtonAXID")]
      performAction:grey_tap()];
}

// TODO(crbug.com/523259471): Fix this flaky test.
// Tests that the contextual panel opens correctly from an IPH.
- (void)testOpenContextualPanelFromIPH {
#if TARGET_IPHONE_SIMULATOR
  if (@available(iOS 18, *)) {
    EARL_GREY_TEST_DISABLED(@"Flaky on iOS 18 simulator, crbug.com/523259471");
  }
#endif

  [ChromeEarlGrey loadURL:self.testServer->GetURL("/defaultresponse")];

  // Check that the IPH has appeared.
  [ChromeEarlGrey
      waitForUIElementToAppearWithMatcher:grey_accessibilityID(
                                              @"BubbleViewLabelIdentifier")];

  [[EarlGrey selectElementWithMatcher:grey_accessibilityID(
                                          @"BubbleViewLabelIdentifier")]
      performAction:grey_tap()];

  // Check that the contextual panel opened up.
  [[EarlGrey
      selectElementWithMatcher:grey_accessibilityID(@"PanelContentViewAXID")]
      assertWithMatcher:grey_sufficientlyVisible()];

  // Close panel
  [[EarlGrey
      selectElementWithMatcher:grey_accessibilityID(@"PanelCloseButtonAXID")]
      performAction:grey_tap()];
}

// TODO(crbug.com/523259471): Fix this flaky test.
// Test that the Contextual Panel can still be closed after rotating to
// landscape.
- (void)testContextualPanelLandscape {
#if TARGET_IPHONE_SIMULATOR
  if (@available(iOS 18, *)) {
    EARL_GREY_TEST_DISABLED(@"Flaky on iOS 18 simulator, crbug.com/523259471");
  }
#endif

  // This test is not relevant on iPads as iPads aren't compact height in
  // landscape.
  if ([ChromeEarlGrey isIPadIdiom]) {
    EARL_GREY_TEST_SKIPPED(@"Skipped for iPad");
  }

  [ChromeEarlGrey loadURL:self.testServer->GetURL("/defaultresponse")];

  [ChromeEarlGrey waitForUIElementToAppearWithMatcher:
                      ContextualPanelEntrypointImageViewMatcher()];

  [[EarlGrey
      selectElementWithMatcher:ContextualPanelEntrypointImageViewMatcher()]
      performAction:grey_tap()];

  // Check that the contextual panel opened up.
  [[EarlGrey
      selectElementWithMatcher:grey_accessibilityID(@"PanelContentViewAXID")]
      assertWithMatcher:grey_sufficientlyVisible()];

  // Switch to landscape.
  GREYAssert(
      [EarlGrey rotateInterfaceToOrientation:UIInterfaceOrientationLandscapeLeft
                                       error:nil],
      @"Could not rotate device to Landscape Left");

  // Make sure that panel can still be closed.
  [[EarlGrey
      selectElementWithMatcher:grey_accessibilityID(@"PanelCloseButtonAXID")]
      performAction:grey_tap()];
}

// TODO(crbug.com/523259471): Fix this flaky test.
// Tests that closing the last tab with the panel open doesn't crash.
- (void)testCloseLastTabWithPanelOpen {
#if TARGET_IPHONE_SIMULATOR
  if (@available(iOS 18, *)) {
    EARL_GREY_TEST_DISABLED(@"Flaky on iOS 18 simulator, crbug.com/523259471");
  }
#endif

  [ChromeEarlGrey loadURL:self.testServer->GetURL("/defaultresponse")];

  [ChromeEarlGrey waitForUIElementToAppearWithMatcher:
                      ContextualPanelEntrypointImageViewMatcher()];

  [[EarlGrey
      selectElementWithMatcher:ContextualPanelEntrypointImageViewMatcher()]
      performAction:grey_tap()];

  // Check that the contextual panel opened up.
  [[EarlGrey
      selectElementWithMatcher:grey_accessibilityID(@"PanelContentViewAXID")]
      assertWithMatcher:grey_sufficientlyVisible()];

  // Close the tab.
  [ChromeEarlGrey closeTabAtIndex:0];
}

// TODO(crbug.com/523259471): Fix this flaky test.
// Tests that closing the last tab before the large entrypoint callback is run
// doesn't crash.
- (void)testCloseLastTabBeforeLargeEntrypointAppears {
#if TARGET_IPHONE_SIMULATOR
  if (@available(iOS 18, *)) {
    EARL_GREY_TEST_DISABLED(@"Flaky on iOS 18 simulator, crbug.com/523259471");
  }
#endif

  [ChromeEarlGrey loadURL:self.testServer->GetURL("/defaultresponse")];

  // Close the tab.
  [ChromeEarlGrey closeTabAtIndex:0];
}

// TODO(crbug.com/523259471): Fix this flaky test.
// Tests that the contextual panel transitions neatly between iOS sheet
// controller (full iPad layout) and the panel's custom sheet component (other
// window open/iPhone-style layout).
- (void)testContexutalPaneliPadMultiwindow {
#if TARGET_IPHONE_SIMULATOR
  if (@available(iOS 18, *)) {
    EARL_GREY_TEST_DISABLED(@"Flaky on iOS 18 simulator, crbug.com/523259471");
  }
#endif

  if (@available(iOS 26.0, *)) {
    // TODO(crbug.com/427699033): Re-enable test on iOS 26.
    // Fails because it assumes a window will be compact after creating a new
    // window.
    EARL_GREY_TEST_DISABLED(@"Test disabled on iOS 26.");
  }

  if (![ChromeEarlGrey areMultipleWindowsSupported]) {
    EARL_GREY_TEST_DISABLED(@"Multiple windows can't be opened.");
  }

  [ChromeEarlGrey loadURL:self.testServer->GetURL("/defaultresponse")];

  [ChromeEarlGrey waitForUIElementToAppearWithMatcher:
                      ContextualPanelEntrypointImageViewMatcher()];

  [[EarlGrey
      selectElementWithMatcher:ContextualPanelEntrypointImageViewMatcher()]
      performAction:grey_tap()];

  // Check that the contextual panel opened up.
  [[EarlGrey
      selectElementWithMatcher:grey_accessibilityID(@"PanelContentViewAXID")]
      assertWithMatcher:grey_sufficientlyVisible()];

  // Open a second window.
  [ChromeEarlGrey openNewWindow];
  [ChromeEarlGrey waitUntilReadyWindowWithNumber:1];
  [ChromeEarlGrey waitForForegroundWindowCount:2];

  // Check that the panel is still visible in the first window
  [[EarlGrey
      selectElementWithMatcher:grey_accessibilityID(@"PanelCloseButtonAXID")]
      assertWithMatcher:grey_sufficientlyVisible()];

  // Close the second window.
  [ChromeEarlGrey closeWindowWithNumber:1];
  [ChromeEarlGrey waitForForegroundWindowCount:1];

  // Check that the panel is still visible in the first window
  [[EarlGrey
      selectElementWithMatcher:grey_accessibilityID(@"PanelContentViewAXID")]
      assertWithMatcher:grey_sufficientlyVisible()];

  [[EarlGrey
      selectElementWithMatcher:grey_accessibilityID(@"PanelCloseButtonAXID")]
      performAction:grey_tap()];
}

// TODO(crbug.com/523259471): Fix this flaky test.
// Tests that fullscreen is disabled when the omnibox switches to bottom
// position.
- (void)testBottomOmniboxDisablesFullscreen {
#if TARGET_IPHONE_SIMULATOR
  if (@available(iOS 18, *)) {
    EARL_GREY_TEST_DISABLED(@"Flaky on iOS 18 simulator, crbug.com/523259471");
  }
#endif

  if (![ChromeEarlGrey isBottomOmniboxAvailable]) {
    EARL_GREY_TEST_SKIPPED(@"Test requires bottom omnibox");
  }

  [ChromeEarlGrey loadURL:self.testServer->GetURL("/long-fullscreen")];

  [ChromeEarlGrey waitForUIElementToAppearWithMatcher:
                      ContextualPanelEntrypointImageViewMatcher()];

  [[EarlGrey
      selectElementWithMatcher:ContextualPanelEntrypointImageViewMatcher()]
      performAction:grey_tap()];

  // Check that the contextual panel opened up.
  [[EarlGrey
      selectElementWithMatcher:grey_accessibilityID(@"PanelContentViewAXID")]
      assertWithMatcher:grey_sufficientlyVisible()];

  // Scroll up in the webpage to enter fullscreen
  [[EarlGrey
      selectElementWithMatcher:chrome_test_util::WebStateScrollViewMatcher()]
      performAction:grey_swipeFastInDirection(kGREYDirectionUp)];
  [ChromeEarlGreyUI waitForToolbarVisible:NO];

  // Enable bottom omnibox.
  [ChromeEarlGrey setBoolValue:YES
             forLocalStatePref:omnibox::kIsOmniboxInBottomPosition];
  [ChromeEarlGreyUI waitForToolbarVisible:YES];

  // Make sure that panel can still be closed.
  [[EarlGrey
      selectElementWithMatcher:grey_accessibilityID(@"PanelCloseButtonAXID")]
      performAction:grey_tap()];
}

// TODO(crbug.com/523259471): Fix this flaky test.
// Test that the Contextual Panel entrypoint's large chip can be dismissed via
// swipe.
- (void)testContextualPanelEntrypointLargeChipDismissable {
#if TARGET_IPHONE_SIMULATOR
  if (@available(iOS 18, *)) {
    EARL_GREY_TEST_DISABLED(@"Flaky on iOS 18 simulator, crbug.com/523259471");
  }
#endif

  [ChromeEarlGrey loadURL:self.testServer->GetURL("/defaultresponse")];

  NSString* entryPointLabel = kLocationBarBadgeLabelIdentifier;
  id<GREYMatcher> entryPointMatcher = grey_allOf(
      grey_accessibilityID(entryPointLabel), grey_sufficientlyVisible(), nil);
  [ChromeEarlGrey
      waitForSufficientlyVisibleElementWithMatcher:entryPointMatcher];

  // Side swipe on the entrypoint.
  [[EarlGrey selectElementWithMatcher:entryPointMatcher]
      performAction:grey_swipeSlowInDirectionWithStartPoint(kGREYDirectionLeft,
                                                            0.9, 0.5)];

  // Check that the entrypoint is now back to default size.
  [[EarlGrey selectElementWithMatcher:entryPointMatcher]
      assertWithMatcher:grey_notVisible()];
}

// TODO(crbug.com/523259471): Fix this flaky test.
// Test that the Contextual Panel entrypoint IPH is dismissed when the device
// orientation changes.
- (void)testOrientationChangeDismissesIPH {
#if TARGET_IPHONE_SIMULATOR
  if (@available(iOS 18, *)) {
    EARL_GREY_TEST_DISABLED(@"Flaky on iOS 18 simulator, crbug.com/523259471");
  }
#endif

  [ChromeEarlGrey loadURL:self.testServer->GetURL("/defaultresponse")];

  // Check that the IPH has appeared.
  [ChromeEarlGrey
      waitForUIElementToAppearWithMatcher:grey_accessibilityID(
                                              @"BubbleViewLabelIdentifier")];

  // Switch to landscape.
  GREYAssert(
      [EarlGrey rotateInterfaceToOrientation:UIInterfaceOrientationLandscapeLeft
                                       error:nil],
      @"Could not rotate device to Landscape Left");

  // Check that the IPH has disappeared.
  [ChromeEarlGrey
      waitForUIElementToDisappearWithMatcher:grey_accessibilityID(
                                                 @"BubbleViewLabelIdentifier")];
}

// TODO(crbug.com/523259471): Fix this flaky test.
// Tests that opening the keyboard on iPhone closes the panel. On iPad, the
// panel is presented modally, so the panel wouldn't close.
- (void)testKeyboardOpenClosesPanelOniPhone {
#if TARGET_IPHONE_SIMULATOR
  if (@available(iOS 18, *)) {
    EARL_GREY_TEST_DISABLED(@"Flaky on iOS 18 simulator, crbug.com/523259471");
  }
#endif

  if ([ChromeEarlGrey isIPadIdiom]) {
    EARL_GREY_TEST_SKIPPED(@"Test conditions don't happen on iPad.");
  }

  // Open a page wth a text field.
  [ChromeEarlGrey loadURL:self.testServer->GetURL("/simple_login_form.html")];

  [ChromeEarlGrey waitForUIElementToAppearWithMatcher:
                      ContextualPanelEntrypointImageViewMatcher()];

  [[EarlGrey
      selectElementWithMatcher:ContextualPanelEntrypointImageViewMatcher()]
      performAction:grey_tap()];

  // Check that the contextual panel opened up.
  [[EarlGrey
      selectElementWithMatcher:grey_accessibilityID(@"PanelContentViewAXID")]
      assertWithMatcher:grey_sufficientlyVisible()];

  // Open the keyboard by tapping a text field.
  [[EarlGrey selectElementWithMatcher:chrome_test_util::WebViewMatcher()]
      performAction:chrome_test_util::TapWebElementWithId("un")];

  // Check that the contextual panel is closed.
  [[EarlGrey
      selectElementWithMatcher:grey_accessibilityID(@"PanelContentViewAXID")]
      assertWithMatcher:grey_nil()];
}

@end
