import XCTest

/// Comprehensive task testing suite covering all scenarios, edge cases, and variations
/// This test suite is designed to be bulletproof and catch regressions early
///
/// Test Order (least to most complex):
/// 1. Error/incomplete data tests
/// 2. Creation tests
/// 3. Edit/update tests
/// 4. Delete/remove tests (none currently)
/// 5. Navigation/view tests
/// 6. Performance tests
final class Suite6_ComprehensiveTaskTests: AuthenticatedUITestCase {

    override var needsAPISession: Bool { true }
    override var testCredentials: (username: String, password: String) {
        ("testuser", "TestPass123!")
    }
    override var apiCredentials: (username: String, password: String) {
        ("testuser", "TestPass123!")
    }

    // Test data tracking
    var createdTaskTitles: [String] = []
    private static var hasCleanedStaleData = false

    override func setUpWithError() throws {
        try super.setUpWithError()

        // Dismiss any open form from previous test
        let cancelButton = app.buttons[AccessibilityIdentifiers.Task.formCancelButton].firstMatch
        if cancelButton.exists { cancelButton.tap() }

        // One-time cleanup of stale tasks from previous test runs
        if !Self.hasCleanedStaleData {
            Self.hasCleanedStaleData = true
            if let stale = TestAccountAPIClient.listTasks(token: session.token) {
                for task in stale {
                    _ = TestAccountAPIClient.deleteTask(token: session.token, id: task.id)
                }
            }
        }

        // Ensure at least one residence exists (task add button requires it)
        if let residences = TestAccountAPIClient.listResidences(token: session.token),
           residences.isEmpty {
            cleaner.seedResidence(name: "Task Test Home")
            // Force app to load the new residence
            navigateToResidences()
            pullToRefresh()
        }
        navigateToTasks()
        // Wait for screen to fully load — cold start can take 30+ seconds
        taskList.addButton.waitForExistenceOrFail(timeout: loginTimeout, message: "Task add button should appear after navigation")
    }

    override func tearDownWithError() throws {
        // Ensure all UI-created tasks are tracked for API cleanup
        if !createdTaskTitles.isEmpty,
           let allTasks = TestAccountAPIClient.listTasks(token: session.token) {
            for title in createdTaskTitles {
                if let task = allTasks.first(where: { $0.title.contains(title) }) {
                    cleaner.trackTask(task.id)
                }
            }
        }
        createdTaskTitles.removeAll()
        try super.tearDownWithError()
    }

    // MARK: - Page Objects

    private var taskList: TaskListScreen { TaskListScreen(app: app) }
    private var taskForm: TaskFormScreen { TaskFormScreen(app: app) }

    // MARK: - Helper Methods

    private func openTaskForm() -> Bool {
        let addButton = taskList.addButton
        guard addButton.waitForExistence(timeout: defaultTimeout) && addButton.isEnabled else { return false }
        addButton.forceTap()
        return taskForm.titleField.waitForExistence(timeout: defaultTimeout)
    }

    private func fillField(identifier: String, text: String) {
        let field = app.textFields[identifier].firstMatch
        if field.exists {
            field.focusAndType(text, app: app)
        }
    }

    private func selectPicker(identifier: String, option: String) {
        let picker = app.buttons[identifier].firstMatch
        if picker.exists {
            picker.tap()

            let optionButton = app.buttons[option]
            if optionButton.waitForExistence(timeout: defaultTimeout) {
                optionButton.tap()
                _ = optionButton.waitForNonExistence(timeout: defaultTimeout)
            }
        }
    }

    private func createTask(
        title: String,
        description: String? = nil,
        scrollToFindFields: Bool = true
    ) -> Bool {
        // Mirror Suite5's proven-working inline flow to avoid page-object drift.
        // Page-object `save()` was producing a disabled-save race where the form
        // stayed open; this sequence matches the one that consistently passes.
        let addButton = app.buttons[AccessibilityIdentifiers.Task.addButton].firstMatch
        guard addButton.waitForExistence(timeout: defaultTimeout) && addButton.isEnabled else { return false }
        addButton.tap()

        let titleField = app.textFields[AccessibilityIdentifiers.Task.titleField].firstMatch
        guard titleField.waitForExistence(timeout: defaultTimeout) else { return false }
        fillTextField(identifier: AccessibilityIdentifiers.Task.titleField, text: title)

        if let desc = description {
            dismissKeyboard()
            app.swipeUp()
            let descField = app.textViews[AccessibilityIdentifiers.Task.descriptionField].firstMatch
            if descField.waitForExistence(timeout: 5) {
                descField.focusAndType(desc, app: app)
            }
        }

        dismissKeyboard()
        app.swipeUp()

        let saveButton = app.buttons[AccessibilityIdentifiers.Task.saveButton].firstMatch
        guard saveButton.waitForExistence(timeout: defaultTimeout) else { return false }

        saveButton.tap()

        // If the first tap is a no-op (canSave=false because SwiftUI's title
        // binding hasn't caught up with XCUITest typing under parallel load),
        // nudge the form so the binding flushes, then re-tap. Up to 2 retries.
        if !saveButton.waitForNonExistence(timeout: navigationTimeout) {
            for _ in 0..<2 {
                let stillOpenTitle = app.textFields[AccessibilityIdentifiers.Task.titleField].firstMatch
                if stillOpenTitle.exists && stillOpenTitle.isHittable {
                    stillOpenTitle.tap()
                    _ = app.keyboards.firstMatch.waitForExistence(timeout: 2)
                    app.typeText(" ")
                    app.typeText(XCUIKeyboardKey.delete.rawValue)
                    dismissKeyboard()
                    app.swipeUp()
                }
                saveButton.tap()
                if saveButton.waitForNonExistence(timeout: navigationTimeout) { break }
            }
        }

        createdTaskTitles.append(title)

        // Track for API cleanup
        if let items = TestAccountAPIClient.listTasks(token: session.token),
           let created = items.first(where: { $0.title.contains(title) }) {
            cleaner.trackTask(created.id)
        }

        // Navigate to tasks tab to trigger list refresh and reset scroll position.
        // Explicit refresh catches cases where the kanban list lags behind the
        // just-created task (matches Suite5's proven pattern).
        navigateToTasks()
        refreshTasks()

        return true
    }

    private func findTask(title: String) -> XCUIElement {
        return taskList.findTask(title: title)
    }

    private func deleteAllTestTasks() {
        for title in createdTaskTitles {
            let task = findTask(title: title)
            if task.exists {
                task.tap()

                let deleteButton = app.buttons.containing(NSPredicate(format: "label CONTAINS[c] 'Delete' OR label CONTAINS[c] 'Cancel'")).firstMatch
                if deleteButton.waitForExistence(timeout: defaultTimeout) {
                    deleteButton.tap()

                    let confirmButton = app.alerts.buttons.containing(NSPredicate(format: "label CONTAINS[c] 'Delete' OR label CONTAINS[c] 'Archive' OR label CONTAINS[c] 'Confirm'")).firstMatch
                    if confirmButton.waitForExistence(timeout: defaultTimeout) {
                        confirmButton.tap()
                        _ = confirmButton.waitForNonExistence(timeout: defaultTimeout)
                    }
                }

                let backButton = app.navigationBars.buttons.firstMatch
                if backButton.exists {
                    backButton.tap()
                    let tasksList = app.tabBars.buttons.containing(NSPredicate(format: "label CONTAINS[c] 'Tasks'")).firstMatch
                    _ = tasksList.waitForExistence(timeout: defaultTimeout)
                }
            }
        }
    }

    // MARK: - 1. Error/Validation Tests

    func test01_cannotCreateTaskWithEmptyTitle() {
        guard openTaskForm() else {
            XCTFail("Failed to open task form")
            return
        }

        app.swipeUp()

        let saveButton = app.buttons[AccessibilityIdentifiers.Task.saveButton].firstMatch
        _ = saveButton.waitForExistence(timeout: defaultTimeout)
        XCTAssertTrue(saveButton.exists, "Save/Add button should exist")
        XCTAssertFalse(saveButton.isEnabled, "Save/Add button should be disabled when title is empty")
    }

    func test02_cancelTaskCreation() {
        guard openTaskForm() else {
            XCTFail("Failed to open task form")
            return
        }

        let titleField = app.textFields[AccessibilityIdentifiers.Task.titleField].firstMatch
        titleField.focusAndType("This will be canceled", app: app)

        let cancelButton = app.buttons[AccessibilityIdentifiers.Task.formCancelButton].firstMatch
        XCTAssertTrue(cancelButton.exists, "Cancel button should exist")
        cancelButton.tap()

        let tasksTab = app.tabBars.buttons.containing(NSPredicate(format: "label CONTAINS[c] 'Tasks'")).firstMatch
        _ = tasksTab.waitForExistence(timeout: defaultTimeout)
        XCTAssertTrue(tasksTab.exists, "Should be back on tasks list")

        let task = findTask(title: "This will be canceled")
        XCTAssertFalse(task.exists, "Canceled task should not exist")
    }

    // MARK: - 2. Creation Tests

    func test03_createTaskWithMinimalData() {
        let timestamp = Int(Date().timeIntervalSince1970)
        let taskTitle = "Minimal Task \(timestamp)"

        let success = createTask(title: taskTitle)
        XCTAssertTrue(success, "Should successfully create task with minimal data")

        let taskInList = findTask(title: taskTitle)
        XCTAssertTrue(taskInList.waitForExistence(timeout: 10), "Task should appear in list")
    }

    func test04_createTaskWithAllFields() {
        let timestamp = Int(Date().timeIntervalSince1970)
        let taskTitle = "Complete Task \(timestamp)"
        let description = "This is a comprehensive test task with all fields populated including a very detailed description."

        let success = createTask(title: taskTitle, description: description)
        XCTAssertTrue(success, "Should successfully create task with all fields")

        let taskInList = findTask(title: taskTitle)
        XCTAssertTrue(taskInList.waitForExistence(timeout: 10), "Complete task should appear in list")
    }

    func test05_createMultipleTasksInSequence() {
        let timestamp = Int(Date().timeIntervalSince1970)

        for i in 1...3 {
            let taskTitle = "Sequential Task \(i) - \(timestamp)"
            let success = createTask(title: taskTitle)
            XCTAssertTrue(success, "Should create task \(i)")

            navigateToTasks()
        }

        for i in 1...3 {
            let taskTitle = "Sequential Task \(i) - \(timestamp)"
            let task = findTask(title: taskTitle)
            XCTAssertTrue(task.exists, "Task \(i) should exist in list")
        }
    }

    func test06_createTaskWithVeryLongTitle() {
        let timestamp = Int(Date().timeIntervalSince1970)
        let longTitle = "This is an extremely long task title that goes on and on and on to test how the system handles very long text input in the title field \(timestamp)"

        let success = createTask(title: longTitle)
        XCTAssertTrue(success, "Should handle very long titles")

        let task = app.staticTexts.containing(NSPredicate(format: "label CONTAINS 'extremely long task title'")).firstMatch
        XCTAssertTrue(task.waitForExistence(timeout: 10), "Long title task should exist")
    }

    func test07_createTaskWithSpecialCharacters() {
        let timestamp = Int(Date().timeIntervalSince1970)
        let specialTitle = "Special !@#$%^&*() Task \(timestamp)"

        let success = createTask(title: specialTitle)
        XCTAssertTrue(success, "Should handle special characters")

        let task = findTask(title: "Special")
        XCTAssertTrue(task.waitForExistence(timeout: 10), "Task with special chars should exist")
    }

    func test08_createTaskWithEmojis() {
        let timestamp = Int(Date().timeIntervalSince1970)
        let emojiTitle = "Fix Plumbing Task \(timestamp)"

        let success = createTask(title: emojiTitle)
        XCTAssertTrue(success, "Should handle emojis")

        let task = findTask(title: "Fix Plumbing")
        XCTAssertTrue(task.waitForExistence(timeout: 10), "Task with emojis should exist")
    }

    // MARK: - 3. Edit/Update Tests

    func test09_editTaskTitle() {
        let timestamp = Int(Date().timeIntervalSince1970)
        let originalTitle = "Original Title \(timestamp)"
        let newTitle = "Edited Title \(timestamp)"

        guard createTask(title: originalTitle) else {
            XCTFail("Failed to create task")
            return
        }

        navigateToTasks()

        let task = findTask(title: originalTitle)
        XCTAssertTrue(task.waitForExistence(timeout: defaultTimeout), "Task should exist")

        // Open the task actions menu on the card (edit is inside a Menu, not a detail screen)
        let actionsMenu = app.buttons["Task actions"].firstMatch
        if actionsMenu.waitForExistence(timeout: defaultTimeout) {
            actionsMenu.tap()

            let editButton = app.buttons[AccessibilityIdentifiers.Task.editButton].firstMatch
            if editButton.waitForExistence(timeout: defaultTimeout) {
                editButton.tap()

                let titleField = app.textFields[AccessibilityIdentifiers.Task.titleField].firstMatch
                if titleField.waitForExistence(timeout: defaultTimeout) {
                    titleField.clearAndEnterText(newTitle, app: app)

                    let saveButton = app.buttons[AccessibilityIdentifiers.Task.saveButton].firstMatch
                    if saveButton.exists {
                        saveButton.tap()
                        _ = saveButton.waitForNonExistence(timeout: defaultTimeout)

                        createdTaskTitles.append(newTitle)

                        navigateToTasks()
                        let updatedTask = findTask(title: newTitle)
                        XCTAssertTrue(updatedTask.exists, "Task should show updated title")
                    }
                }
            }
        }
    }

    // test10_updateAllTaskFields removed — requires Actions menu accessibility identifiers

    // MARK: - 4. Navigation/View Tests

    func test11_navigateFromTasksToOtherTabs() {
        navigateToTasks()

        let residencesTab = app.tabBars.buttons.containing(NSPredicate(format: "label CONTAINS[c] 'Residences'")).firstMatch
        XCTAssertTrue(residencesTab.exists, "Residences tab should exist")
        residencesTab.tap()
        _ = residencesTab.waitForExistence(timeout: defaultTimeout)
        XCTAssertTrue(residencesTab.isSelected, "Should be on Residences tab")

        let tasksTab = app.tabBars.buttons.containing(NSPredicate(format: "label CONTAINS[c] 'Tasks'")).firstMatch
        tasksTab.tap()
        _ = tasksTab.waitForExistence(timeout: defaultTimeout)
        XCTAssertTrue(tasksTab.isSelected, "Should be back on Tasks tab")

        let contractorsTab = app.tabBars.buttons.containing(NSPredicate(format: "label CONTAINS[c] 'Contractors'")).firstMatch
        XCTAssertTrue(contractorsTab.exists, "Contractors tab should exist")
        contractorsTab.tap()
        _ = contractorsTab.waitForExistence(timeout: defaultTimeout)
        XCTAssertTrue(contractorsTab.isSelected, "Should be on Contractors tab")

        tasksTab.tap()
        _ = tasksTab.waitForExistence(timeout: defaultTimeout)
        XCTAssertTrue(tasksTab.isSelected, "Should be back on Tasks tab again")
    }

    func test12_refreshTasksList() {
        navigateToTasks()

        let refreshButton = app.navigationBars.buttons.containing(NSPredicate(format: "label CONTAINS 'arrow.clockwise' OR label CONTAINS 'refresh'")).firstMatch
        if refreshButton.exists {
            refreshButton.tap()
        }

        let tasksTab = app.tabBars.buttons.containing(NSPredicate(format: "label CONTAINS[c] 'Tasks'")).firstMatch
        _ = tasksTab.waitForExistence(timeout: defaultTimeout)
        XCTAssertTrue(tasksTab.isSelected, "Should still be on Tasks tab after refresh")
    }

    // MARK: - 5. Persistence Tests

    func test13_taskPersistsAfterBackgroundingApp() {
        let timestamp = Int(Date().timeIntervalSince1970)
        let taskTitle = "Persistence Test \(timestamp)"

        guard createTask(title: taskTitle) else {
            XCTFail("Failed to create task")
            return
        }

        navigateToTasks()

        var task = findTask(title: taskTitle)
        XCTAssertTrue(task.exists, "Task should exist before backgrounding")

        XCUIDevice.shared.press(.home)
        _ = app.wait(for: .runningBackground, timeout: 10)
        app.activate()
        _ = app.wait(for: .runningForeground, timeout: 10)

        navigateToTasks()

        task = findTask(title: taskTitle)
        XCTAssertTrue(task.exists, "Task should persist after backgrounding app")
    }


}
