//
// Copyright 2023 New Vector Ltd
//
// 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 XCTest

class UserFlowTests: XCTestCase {
    private var app: XCUIApplication!
    
    override func setUp() {
        app = Application.launch()
        app.login(currentTestCase: self)
    }
    
    func testUserFlow() {
        checkSettings()
        
        checkInvites()
        
        checkRoomCreation()
        
        // Open the first room in the list.
        let firstRoom = app.buttons.matching(NSPredicate(format: "identifier BEGINSWITH %@", A11yIdentifiers.homeScreen.roomNamePrefix)).firstMatch
        XCTAssertTrue(firstRoom.waitForExistence(timeout: 10.0))
        firstRoom.tap()
        
        checkPhotoSharing()
        
        checkDocumentSharing()
        
        checkLocationSharing()
        
        checkTimelineItemActionMenu()
        
        checkRoomDetails()
        
        app.logout()
    }
    
    private func checkInvites() {
        // Open invites
        let invitesButton = app.buttons[A11yIdentifiers.homeScreen.invites]
        XCTAssertTrue(invitesButton.waitForExistence(timeout: 10.0))
        invitesButton.tap()
        
        // Go back to the room list
        tapOnBackButton("Chats")
    }
    
    private func checkPhotoSharing() {
        // Open attachments picker
        tapOnMenu(A11yIdentifiers.roomScreen.composerToolbar.openComposeOptions)

        // Open photo library picker
        tapOnButton(A11yIdentifiers.roomScreen.attachmentPickerPhotoLibrary)
        
        // Tap on the second image. First one is always broken on simulators.
        let secondImage = app.scrollViews.images.element(boundBy: 1)
        XCTAssertTrue(secondImage.waitForExistence(timeout: 10.0)) // Photo library takes a bit to load
        secondImage.tap()
        
        // Cancel the upload flow
        tapOnButton("Cancel", waitForDisappearance: true)
    }
    
    private func checkDocumentSharing() {
        tapOnMenu(A11yIdentifiers.roomScreen.composerToolbar.openComposeOptions)
        tapOnButton(A11yIdentifiers.roomScreen.attachmentPickerDocuments)
        
        tapOnButton("Cancel", waitForDisappearance: true)
    }
    
    private func checkLocationSharing() {
        tapOnMenu(A11yIdentifiers.roomScreen.composerToolbar.openComposeOptions)
        tapOnButton(A11yIdentifiers.roomScreen.attachmentPickerLocation)
        
        // The order of the alerts is a bit of a mistery so try twice
        
        allowLocationPermissionOnce()
        
        // Handle map loading errors (missing credentials)
        let alertOkButton = app.alerts.firstMatch.buttons["OK"].firstMatch
        if alertOkButton.waitForExistence(timeout: 10.0) {
            alertOkButton.tap()
        }
        
        allowLocationPermissionOnce()
        
        tapOnButton("Cancel", waitForDisappearance: true)
    }
    
    private func allowLocationPermissionOnce() {
        let springboard = XCUIApplication(bundleIdentifier: "com.apple.springboard")
        let notificationAlertAllowButton = springboard.buttons["Allow Once"].firstMatch
        if notificationAlertAllowButton.waitForExistence(timeout: 10.0) {
            notificationAlertAllowButton.tap()
        }
    }
    
    private func checkRoomCreation() {
        tapOnButton(A11yIdentifiers.homeScreen.startChat)
        
        tapOnButton(A11yIdentifiers.startChatScreen.createRoom)
        
        tapOnButton(A11yIdentifiers.inviteUsersScreen.proceed)
        
        tapOnBackButton("Invite people")
        
        tapOnBackButton("Start chat")
        
        tapOnButton("Cancel", waitForDisappearance: true)
    }
    
    private func checkTimelineItemActionMenu() {
        // Long press on the last message
        let lastMessage = app.cells.firstMatch
        XCTAssertTrue(lastMessage.waitForExistence(timeout: 10.0))
        lastMessage.press(forDuration: 2.0)
        
        // Hide the bottom sheet
        let timelineItemActionMenu = app.scrollViews[A11yIdentifiers.roomScreen.timelineItemActionMenu].firstMatch
        
        // Some rooms might not have any messages in it (e.g. message history unavailable)
        if timelineItemActionMenu.waitForExistence(timeout: 10.0) {
            timelineItemActionMenu.swipeDown(velocity: .fast)
        }
    }
    
    private func checkRoomDetails() {
        // Open the room details
        let roomHeader = app.staticTexts[A11yIdentifiers.roomScreen.name]
        XCTAssertTrue(roomHeader.waitForExistence(timeout: 10.0))
        roomHeader.tap()
        
        // Open the room member details
        tapOnButton(A11yIdentifiers.roomDetailsScreen.people)
        
        // Open the first member's details. Loading members for big rooms can take a while.
        let firstRoomMember = app.scrollViews.buttons.firstMatch
        XCTAssertTrue(firstRoomMember.waitForExistence(timeout: 1000.0))
        firstRoomMember.tap()
        
        // Go back to the room member details
        tapOnBackButton("People")
        
        // Go back to the room details
        tapOnBackButton()
        
        // Go back to the room
        tapOnBackButton()
        
        // Go back to the room list
        tapOnBackButton("Chats")
    }
    
    private func checkSettings() {
        // On first login when multiple sheets get presented the profile button is not hittable
        // Moving the scroll fixed it for some obscure reason
        app.swipeDown()
        
        let profileButton = app.buttons[A11yIdentifiers.homeScreen.userAvatar]
        
        // `Failed to scroll to visible (by AX action) Button` https://stackoverflow.com/a/33534187/730924
        profileButton.forceTap()
        
        // Open analytics
        tapOnButton(A11yIdentifiers.settingsScreen.analytics)
        
        // Go back to settings
        tapOnBackButton("Settings")
        
        // Open report a bug
        tapOnButton(A11yIdentifiers.settingsScreen.reportBug)
        
        // Go back to settings
        tapOnBackButton("Settings")
        
        // Open about
        tapOnButton(A11yIdentifiers.settingsScreen.about)
        
        // Go back to settings
        tapOnBackButton("Settings")
        
        // Close the settings
        tapOnButton(A11yIdentifiers.settingsScreen.done)
    }
    
    private func tapOnButton(_ identifier: String, waitForDisappearance: Bool = false) {
        let button = app.buttons[identifier]
        XCTAssertTrue(button.waitForExistence(timeout: 10.0))
        button.tap()
        
        if waitForDisappearance {
            let doesNotExistPredicate = NSPredicate(format: "exists == 0")
            expectation(for: doesNotExistPredicate, evaluatedWith: button)
            waitForExpectations(timeout: 10.0)
        }
    }
    
    private func tapOnMenu(_ identifier: String) {
        let button = app.buttons[identifier]
        XCTAssertTrue(button.waitForExistence(timeout: 10.0))
        button.forceTap()
    }
    
    /// Taps on a back button that the system configured with a label but no identifier.
    ///
    /// When there are multiple buttons with the same label in the hierarchy, all the buttons we created
    /// should have an identifier set, and so this method will ignore those picking the one with only a label.
    private func tapOnBackButton(_ label: String = "Back") {
        let button = app.buttons.matching(NSPredicate(format: "label == %@ && identifier == ''", label)).firstMatch
        XCTAssertTrue(button.waitForExistence(timeout: 10.0))
        button.tap()
    }
}
