import XCTest

/// Comprehensive residence 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 Suite4_ComprehensiveResidenceTests: AuthenticatedUITestCase {

    override var needsAPISession: Bool { true }

    // Test data tracking
    var createdResidenceNames: [String] = []

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

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

        navigateToResidences()
        residenceList.addButton.waitForExistenceOrFail(timeout: navigationTimeout, message: "Residence add button should appear after navigation")
    }

    override func tearDownWithError() throws {
        // Ensure all UI-created residences are tracked for API cleanup
        if !createdResidenceNames.isEmpty,
           let allResidences = TestAccountAPIClient.listResidences(token: session.token) {
            for name in createdResidenceNames {
                if let res = allResidences.first(where: { $0.name.contains(name) }) {
                    cleaner.trackResidence(res.id)
                }
            }
        }
        createdResidenceNames.removeAll()
        try super.tearDownWithError()
    }

    // MARK: - Page Objects

    private var residenceList: ResidenceListScreen { ResidenceListScreen(app: app) }
    private var residenceForm: ResidenceFormScreen { ResidenceFormScreen(app: app) }
    private var residenceDetail: ResidenceDetailScreen { ResidenceDetailScreen(app: app) }

    // MARK: - Helper Methods

    private func openResidenceForm(file: StaticString = #filePath, line: UInt = #line) {
        let addButton = residenceList.addButton
        addButton.waitForExistenceOrFail(timeout: defaultTimeout, message: "Residence add button should exist", file: file, line: line)
        XCTAssertTrue(addButton.isEnabled, "Residence add button should be enabled", file: file, line: line)
        addButton.tap()
        residenceForm.nameField.waitForExistenceOrFail(timeout: defaultTimeout, message: "Residence form should open", file: file, line: line)
    }

    /// Fill sequential address fields using the Return key to advance focus.
    /// Fill address fields. Dismisses keyboard between each field for clean focus.
    private func fillAddressFields(street: String, city: String, state: String, postal: String) {
        // Scroll address section into view — may need multiple swipes on smaller screens
        let streetField = app.textFields[AccessibilityIdentifiers.Residence.streetAddressField].firstMatch
        for _ in 0..<3 {
            if streetField.exists && streetField.isHittable { break }
            app.swipeUp()
        }
        streetField.waitForExistenceOrFail(timeout: navigationTimeout, message: "Street field should appear after scroll")

        fillTextField(identifier: AccessibilityIdentifiers.Residence.streetAddressField, text: street)
        dismissKeyboard()
        fillTextField(identifier: AccessibilityIdentifiers.Residence.cityField, text: city)
        dismissKeyboard()
        fillTextField(identifier: AccessibilityIdentifiers.Residence.stateProvinceField, text: state)
        dismissKeyboard()
        fillTextField(identifier: AccessibilityIdentifiers.Residence.postalCodeField, text: postal)
        dismissKeyboard()
    }

    private func selectPropertyType(type: String) {
        let picker = app.buttons[AccessibilityIdentifiers.Residence.propertyTypePicker].firstMatch
        guard picker.waitForExistence(timeout: defaultTimeout) else {
            XCTFail("Property type picker not found")
            return
        }
        picker.tap()

        // SwiftUI Picker in Form pushes a selection list — find the option by text
        let option = app.staticTexts[type]
        option.waitForExistenceOrFail(timeout: navigationTimeout, message: "Property type '\(type)' should appear in picker list")
        option.tap()
    }

    private func createResidence(
        name: String,
        propertyType: String? = nil,
        street: String = "123 Test St",
        city: String = "TestCity",
        state: String = "TS",
        postal: String = "12345"
    ) {
        openResidenceForm()

        residenceForm.enterName(name)
        if let propertyType = propertyType {
            selectPropertyType(type: propertyType)
        }
        dismissKeyboard()
        fillAddressFields(street: street, city: city, state: state, postal: postal)
        residenceForm.save()

        createdResidenceNames.append(name)

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

    private func findResidence(name: String) -> XCUIElement {
        return app.staticTexts.containing(NSPredicate(format: "label CONTAINS %@", name)).firstMatch
    }

    // MARK: - 1. Error/Validation Tests

    func test01_cannotCreateResidenceWithEmptyName() {
        openResidenceForm()

        // Leave name empty, fill only address
        fillAddressFields(street: "123 Test St", city: "TestCity", state: "TS", postal: "12345")

        // Scroll to save button if needed
        app.swipeUp()

        // Submit button should be disabled when name is empty (may be labeled "Add" or "Save")
        let saveButton = app.buttons[AccessibilityIdentifiers.Residence.saveButton].firstMatch
        _ = saveButton.waitForExistence(timeout: defaultTimeout)
        XCTAssertTrue(saveButton.exists, "Submit button should exist")
        XCTAssertFalse(saveButton.isEnabled, "Submit button should be disabled when name is empty")

        // Clean up: dismiss the form so next test starts on the list
        residenceForm.cancel()
    }

    func test02_cancelResidenceCreation() {
        openResidenceForm()

        // Fill some data
        let nameField = app.textFields[AccessibilityIdentifiers.Residence.nameField].firstMatch
        nameField.tap()
        // Wait for keyboard to appear before typing
        let keyboard = app.keyboards.firstMatch
        _ = keyboard.waitForExistence(timeout: 3)
        nameField.typeText("This will be canceled")

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

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

        // Residence should not exist
        let residence = findResidence(name: "This will be canceled")
        XCTAssertFalse(residence.exists, "Canceled residence should not exist")
    }

    // MARK: - 2. Creation Tests

    func test03_createResidenceWithMinimalData() {
        let timestamp = Int(Date().timeIntervalSince1970)
        let residenceName = "Minimal Home \(timestamp)"

        createResidence(name: residenceName)

        let residenceInList = findResidence(name: residenceName)
        XCTAssertTrue(residenceInList.waitForExistence(timeout: 10), "Residence should appear in list")
    }

    // test04_createResidenceWithAllPropertyTypes — removed: backend has no seeded residence types

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

        for i in 1...3 {
            let residenceName = "Sequential Home \(i) - \(timestamp)"
            createResidence(name: residenceName)

            navigateToResidences()
        }

        // Verify all residences exist
        for i in 1...3 {
            let residenceName = "Sequential Home \(i) - \(timestamp)"
            let residence = findResidence(name: residenceName)
            XCTAssertTrue(residence.exists, "Residence \(i) should exist in list")
        }
    }

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

        createResidence(name: longName)

        // Verify it appears (may be truncated in display)
        let residence = app.staticTexts.containing(NSPredicate(format: "label CONTAINS 'extremely long residence'")).firstMatch
        XCTAssertTrue(residence.waitForExistence(timeout: 10), "Long name residence should exist")
    }

    func test07_createResidenceWithSpecialCharacters() {
        let timestamp = Int(Date().timeIntervalSince1970)
        let specialName = "Special !@#$%^&*() Home \(timestamp)"

        createResidence(name: specialName)

        let residence = findResidence(name: "Special")
        XCTAssertTrue(residence.waitForExistence(timeout: 10), "Residence with special chars should exist")
    }

    func test08_createResidenceWithEmojis() {
        let timestamp = Int(Date().timeIntervalSince1970)
        let emojiName = "Beach House \(timestamp)"

        createResidence(name: emojiName)

        let residence = findResidence(name: "Beach House")
        XCTAssertTrue(residence.waitForExistence(timeout: 10), "Residence with emojis should exist")
    }

    func test09_createResidenceWithInternationalCharacters() {
        let timestamp = Int(Date().timeIntervalSince1970)
        let internationalName = "Chateau Montreal \(timestamp)"

        createResidence(name: internationalName)

        let residence = findResidence(name: "Chateau")
        XCTAssertTrue(residence.waitForExistence(timeout: 10), "Residence with international chars should exist")
    }

    func test10_createResidenceWithVeryLongAddress() {
        let timestamp = Int(Date().timeIntervalSince1970)
        let residenceName = "Long Address Home \(timestamp)"

        createResidence(
            name: residenceName,
            street: "123456789 Very Long Street Name That Goes On And On Boulevard Apartment Complex Unit 42B",
            city: "VeryLongCityNameThatTestsTheLimit",
            state: "CA",
            postal: "12345-6789"
        )

        let residence = findResidence(name: residenceName)
        XCTAssertTrue(residence.waitForExistence(timeout: 10), "Residence with long address should exist")
    }

    // MARK: - 3. Edit/Update Tests

    func test11_editResidenceName() {
        let timestamp = Int(Date().timeIntervalSince1970)
        let originalName = "Original Name \(timestamp)"
        let newName = "Edited Name \(timestamp)"

        // Create residence
        createResidence(name: originalName)

        navigateToResidences()

        // Find and tap residence
        let residence = findResidence(name: originalName)
        XCTAssertTrue(residence.waitForExistence(timeout: defaultTimeout), "Residence should exist")
        residence.tap()

        // Tap edit button
        let editButton = app.buttons[AccessibilityIdentifiers.Residence.editButton].firstMatch
        if editButton.waitForExistence(timeout: defaultTimeout) {
            editButton.tap()

            // Edit name
            let nameField = app.textFields[AccessibilityIdentifiers.Residence.nameField].firstMatch
            if nameField.waitForExistence(timeout: defaultTimeout) {
                nameField.clearAndEnterText(newName, app: app)

                // Save
                let saveButton = app.buttons[AccessibilityIdentifiers.Residence.saveButton].firstMatch
                if saveButton.exists {
                    saveButton.tap()

                    // Track new name
                    createdResidenceNames.append(newName)

                    // Verify new name appears
                    navigateToResidences()
                    let updatedResidence = findResidence(name: newName)
                    XCTAssertTrue(updatedResidence.waitForExistence(timeout: defaultTimeout), "Residence should show updated name")
                }
            }
        }
    }

    func test12_updateAllResidenceFields() {
        let timestamp = Int(Date().timeIntervalSince1970)
        let originalName = "Update All Fields \(timestamp)"
        let newName = "All Fields Updated \(timestamp)"
        let newStreet = "999 Updated Avenue"
        let newCity = "NewCity"
        let newState = "NC"
        let newPostal = "99999"

        // Create residence with initial values
        createResidence(name: originalName, street: "123 Old St", city: "OldCity", state: "OC", postal: "11111")

        navigateToResidences()

        // Find and tap residence
        let residence = findResidence(name: originalName)
        XCTAssertTrue(residence.waitForExistence(timeout: defaultTimeout), "Residence should exist")
        residence.tap()

        // Tap edit button
        let editButton = app.buttons[AccessibilityIdentifiers.Residence.editButton].firstMatch
        XCTAssertTrue(editButton.waitForExistence(timeout: defaultTimeout), "Edit button should exist")
        editButton.tap()

        // Update name
        let nameField = app.textFields[AccessibilityIdentifiers.Residence.nameField].firstMatch
        XCTAssertTrue(nameField.waitForExistence(timeout: defaultTimeout), "Name field should exist")
        nameField.clearAndEnterText(newName, app: app)

        // Property type update skipped — backend has no seeded residence types

        // Dismiss keyboard from name edit, scroll to address fields
        dismissKeyboard()
        app.swipeUp()

        // Update address fields
        let streetField = app.textFields[AccessibilityIdentifiers.Residence.streetAddressField].firstMatch
        if streetField.waitForExistence(timeout: defaultTimeout) {
            streetField.clearAndEnterText(newStreet, app: app)
            dismissKeyboard()
        }

        let cityField = app.textFields[AccessibilityIdentifiers.Residence.cityField].firstMatch
        if cityField.waitForExistence(timeout: defaultTimeout) {
            cityField.clearAndEnterText(newCity, app: app)
            dismissKeyboard()
        }

        let stateField = app.textFields[AccessibilityIdentifiers.Residence.stateProvinceField].firstMatch
        if stateField.waitForExistence(timeout: defaultTimeout) {
            stateField.clearAndEnterText(newState, app: app)
            dismissKeyboard()
        }

        // Update postal code
        let postalField = app.textFields[AccessibilityIdentifiers.Residence.postalCodeField].firstMatch
        if postalField.exists {
            postalField.clearAndEnterText(newPostal, app: app)
        }

        // Scroll to save button
        app.swipeUp()

        // Save
        let saveButton = app.buttons[AccessibilityIdentifiers.Residence.saveButton].firstMatch
        _ = saveButton.waitForExistence(timeout: defaultTimeout)
        XCTAssertTrue(saveButton.exists, "Save button should exist")
        saveButton.tap()

        // Wait for form to dismiss after API call
        _ = saveButton.waitForNonExistence(timeout: defaultTimeout)

        // Track new name
        createdResidenceNames.append(newName)

        // Verify updated residence appears in list with new name
        navigateToResidences()
        let updatedResidence = findResidence(name: newName)
        XCTAssertTrue(updatedResidence.waitForExistence(timeout: defaultTimeout), "Residence should show updated name in list")

        // Name update verified in list — detail view doesn't display address fields

    }

    // MARK: - 4. View/Navigation Tests

    func test13_viewResidenceDetails() {
        let timestamp = Int(Date().timeIntervalSince1970)
        let residenceName = "Detail View Test \(timestamp)"

        // Create residence
        createResidence(name: residenceName)

        navigateToResidences()

        // Tap on residence
        let residence = findResidence(name: residenceName)
        XCTAssertTrue(residence.waitForExistence(timeout: defaultTimeout), "Residence should exist")
        residence.tap()

        // Verify detail view appears with edit button or tasks section
        let editButton = app.buttons[AccessibilityIdentifiers.Residence.editButton].firstMatch
        let tasksSection = app.staticTexts.containing(NSPredicate(format: "label CONTAINS[c] 'Tasks' OR label CONTAINS[c] 'Maintenance'")).firstMatch

        _ = editButton.waitForExistence(timeout: defaultTimeout)
        XCTAssertTrue(editButton.exists || tasksSection.exists, "Detail view should show with edit button or tasks section")
    }

    func test14_navigateFromResidencesToOtherTabs() {
        // From Residences tab
        navigateToResidences()

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

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

        // Navigate to Contractors
        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")

        // Back to Residences
        residencesTab.tap()
        _ = residencesTab.waitForExistence(timeout: defaultTimeout)
        XCTAssertTrue(residencesTab.isSelected, "Should be back on Residences tab again")
    }

    func test15_refreshResidencesList() {
        navigateToResidences()

        // Pull to refresh (if implemented) or use refresh button
        let refreshButton = app.navigationBars.buttons.containing(NSPredicate(format: "label CONTAINS 'arrow.clockwise' OR label CONTAINS 'refresh'")).firstMatch
        if refreshButton.waitForExistence(timeout: defaultTimeout) {
            refreshButton.tap()
            _ = app.activityIndicators.firstMatch.waitForNonExistence(timeout: defaultTimeout)
        }

        // Verify we're still on residences tab
        let residencesTab = app.tabBars.buttons.containing(NSPredicate(format: "label CONTAINS[c] 'Residences'")).firstMatch
        XCTAssertTrue(residencesTab.isSelected, "Should still be on Residences tab after refresh")
    }

    // MARK: - 5. Persistence Tests

    func test16_residencePersistsAfterBackgroundingApp() {
        let timestamp = Int(Date().timeIntervalSince1970)
        let residenceName = "Persistence Test \(timestamp)"

        // Create residence
        createResidence(name: residenceName)

        navigateToResidences()

        // Verify residence exists
        var residence = findResidence(name: residenceName)
        XCTAssertTrue(residence.waitForExistence(timeout: defaultTimeout), "Residence should exist before backgrounding")

        // Background and reactivate app
        XCUIDevice.shared.press(.home)
        _ = app.wait(for: .runningForeground, timeout: 10)

        // Navigate back to residences
        navigateToResidences()

        // Verify residence still exists
        residence = findResidence(name: residenceName)
        XCTAssertTrue(residence.waitForExistence(timeout: defaultTimeout), "Residence should persist after backgrounding app")
    }

}
