import XCTest

// MARK: - Task Screens

/// Page object for the task list screen (kanban or list view).
struct TaskListScreen {
    let app: XCUIApplication

    var addButton: XCUIElement {
        let byID = app.buttons[AccessibilityIdentifiers.Task.addButton].firstMatch
        if byID.exists { return byID }
        // Fallback: nav bar plus/Add button
        let navBarButtons = app.navigationBars.buttons
        for i in 0..<navBarButtons.count {
            let button = navBarButtons.element(boundBy: i)
            if button.label == "plus" || button.label.contains("Add") {
                if button.isEnabled { return button }
            }
        }
        // Fallback: empty state add button
        let emptyStateButton = app.buttons.containing(NSPredicate(format: "label CONTAINS[c] 'Add Task'")).firstMatch
        if emptyStateButton.exists && emptyStateButton.isEnabled { return emptyStateButton }
        return byID
    }

    var emptyState: XCUIElement {
        app.otherElements[AccessibilityIdentifiers.Task.emptyStateView]
    }

    var tasksList: XCUIElement {
        app.otherElements[AccessibilityIdentifiers.Task.tasksList]
    }

    func waitForLoad(timeout: TimeInterval = 15) {
        let deadline = Date().addingTimeInterval(timeout)
        var loaded = false
        repeat {
            loaded = addButton.exists
                || emptyState.exists
                || tasksList.exists
                || app.staticTexts.containing(NSPredicate(format: "label CONTAINS[c] 'Tasks'")).firstMatch.exists
            if loaded { break }
            RunLoop.current.run(until: Date().addingTimeInterval(0.2))
        } while Date() < deadline
        XCTAssertTrue(loaded, "Expected task list screen to load")
    }

    func openCreateTask() {
        addButton.waitForExistenceOrFail(timeout: 10)
        addButton.forceTap()
    }

    func findTask(title: String) -> XCUIElement {
        let predicate = NSPredicate(format: "label CONTAINS %@", title)
        let match = app.staticTexts.containing(predicate).firstMatch

        // If found immediately, return
        if match.waitForExistence(timeout: 3) { return match }

        // Scroll through kanban columns (swipe left up to 6 times)
        let scrollView = app.scrollViews.firstMatch
        guard scrollView.exists else { return match }

        for _ in 0..<6 {
            scrollView.swipeLeft()
            if match.waitForExistence(timeout: 1) { return match }
        }

        // Scroll back and try right direction
        for _ in 0..<6 {
            scrollView.swipeRight()
            if match.waitForExistence(timeout: 1) { return match }
        }

        return match
    }
}

/// Page object for the task create/edit form.
struct TaskFormScreen {
    let app: XCUIApplication

    var titleField: XCUIElement {
        app.textFields[AccessibilityIdentifiers.Task.titleField]
    }

    var descriptionField: XCUIElement {
        app.textViews[AccessibilityIdentifiers.Task.descriptionField]
    }

    var saveButton: XCUIElement {
        app.buttons[AccessibilityIdentifiers.Task.saveButton]
    }

    var cancelButton: XCUIElement {
        app.buttons[AccessibilityIdentifiers.Task.formCancelButton]
    }

    func waitForLoad(timeout: TimeInterval = 15) {
        XCTAssertTrue(titleField.waitForExistence(timeout: timeout), "Expected task form to load")
    }

    func enterTitle(_ text: String) {
        titleField.waitForExistenceOrFail(timeout: 10)
        titleField.focusAndType(text, app: app)
    }

    func enterDescription(_ text: String) {
        app.swipeUp()
        if descriptionField.waitForExistence(timeout: 5) {
            descriptionField.focusAndType(text, app: app)
        }
    }

    func save() {
        KeyboardDismisser.dismiss(app: app)
        // Scroll the form so any focused-field state commits before the
        // submit action reads it. Without this the title binding can lag.
        app.swipeUp()
        saveButton.waitForExistenceOrFail(timeout: 10)
        saveButton.forceTap()
        _ = saveButton.waitForNonExistence(timeout: 15)
    }

    func cancel() {
        cancelButton.waitForExistenceOrFail(timeout: 10)
        cancelButton.forceTap()
    }
}

// MARK: - Contractor Screens

/// Page object for the contractor list screen.
struct ContractorListScreen {
    let app: XCUIApplication

    var addButton: XCUIElement {
        let byID = app.buttons[AccessibilityIdentifiers.Contractor.addButton]
        if byID.exists { return byID }
        let navBarButtons = app.navigationBars.buttons
        for i in 0..<navBarButtons.count {
            let button = navBarButtons.element(boundBy: i)
            if button.label == "plus" || button.label.contains("Add") {
                if button.isEnabled { return button }
            }
        }
        return app.buttons.containing(NSPredicate(format: "label CONTAINS 'plus'")).firstMatch
    }

    var emptyState: XCUIElement {
        app.otherElements[AccessibilityIdentifiers.Contractor.emptyStateView]
    }

    var contractorsList: XCUIElement {
        app.otherElements[AccessibilityIdentifiers.Contractor.contractorsList]
    }

    func waitForLoad(timeout: TimeInterval = 15) {
        let deadline = Date().addingTimeInterval(timeout)
        var loaded = false
        repeat {
            loaded = addButton.exists
                || emptyState.exists
                || contractorsList.exists
                || app.staticTexts.containing(NSPredicate(format: "label CONTAINS[c] 'Contractors'")).firstMatch.exists
            if loaded { break }
            RunLoop.current.run(until: Date().addingTimeInterval(0.2))
        } while Date() < deadline
        XCTAssertTrue(loaded, "Expected contractor list screen to load")
    }

    func openCreateContractor() {
        addButton.waitForExistenceOrFail(timeout: 10)
        addButton.forceTap()
    }

    func findContractor(name: String) -> XCUIElement {
        app.staticTexts.containing(NSPredicate(format: "label CONTAINS[c] %@", name)).firstMatch
    }
}

/// Page object for the contractor create/edit form.
struct ContractorFormScreen {
    let app: XCUIApplication

    var nameField: XCUIElement {
        app.textFields[AccessibilityIdentifiers.Contractor.nameField]
    }

    var phoneField: XCUIElement {
        app.textFields[AccessibilityIdentifiers.Contractor.phoneField]
    }

    var emailField: XCUIElement {
        app.textFields[AccessibilityIdentifiers.Contractor.emailField]
    }

    var companyField: XCUIElement {
        app.textFields[AccessibilityIdentifiers.Contractor.companyField]
    }

    var saveButton: XCUIElement {
        app.buttons[AccessibilityIdentifiers.Contractor.saveButton]
    }

    var cancelButton: XCUIElement {
        app.buttons[AccessibilityIdentifiers.Contractor.formCancelButton]
    }

    func waitForLoad(timeout: TimeInterval = 15) {
        XCTAssertTrue(nameField.waitForExistence(timeout: timeout), "Expected contractor form to load")
    }

    func enterName(_ text: String) {
        nameField.waitForExistenceOrFail(timeout: 10)
        nameField.focusAndType(text, app: app)
    }

    func enterPhone(_ text: String) {
        if phoneField.waitForExistence(timeout: 5) {
            phoneField.focusAndType(text, app: app)
        }
    }

    func enterEmail(_ text: String) {
        if emailField.waitForExistence(timeout: 5) {
            emailField.focusAndType(text, app: app)
        }
    }

    func enterCompany(_ text: String) {
        if companyField.waitForExistence(timeout: 5) {
            companyField.focusAndType(text, app: app)
        }
    }

    func save() {
        KeyboardDismisser.dismiss(app: app)
        if !saveButton.exists || !saveButton.isHittable { app.swipeUp() }
        saveButton.waitForExistenceOrFail(timeout: 10)
        saveButton.forceTap()
        _ = saveButton.waitForNonExistence(timeout: 15)
    }

    func cancel() {
        cancelButton.waitForExistenceOrFail(timeout: 10)
        cancelButton.forceTap()
    }
}

/// Page object for the contractor detail screen.
struct ContractorDetailScreen {
    let app: XCUIApplication

    var menuButton: XCUIElement {
        let byID = app.buttons[AccessibilityIdentifiers.Contractor.menuButton]
        if byID.exists { return byID }
        return app.images["ellipsis.circle"].firstMatch
    }

    var editButton: XCUIElement {
        app.buttons[AccessibilityIdentifiers.Contractor.editButton]
    }

    var deleteButton: XCUIElement {
        app.buttons[AccessibilityIdentifiers.Contractor.deleteButton]
    }

    func waitForLoad(timeout: TimeInterval = 15) {
        let deadline = Date().addingTimeInterval(timeout)
        var loaded = false
        repeat {
            loaded = menuButton.exists
                || app.staticTexts.containing(NSPredicate(format: "label CONTAINS[c] 'Phone' OR label CONTAINS[c] 'Email'")).firstMatch.exists
            if loaded { break }
            RunLoop.current.run(until: Date().addingTimeInterval(0.2))
        } while Date() < deadline
        XCTAssertTrue(loaded, "Expected contractor detail screen to load")
    }

    func openMenu() {
        menuButton.waitForExistenceOrFail(timeout: 10)
        menuButton.forceTap()
    }

    func tapEdit() {
        openMenu()
        editButton.waitForExistenceOrFail(timeout: 10)
        editButton.forceTap()
    }

    func tapDelete() {
        openMenu()
        deleteButton.waitForExistenceOrFail(timeout: 10)
        deleteButton.forceTap()
    }
}

// MARK: - Document Screens

/// Page object for the document list screen.
struct DocumentListScreen {
    let app: XCUIApplication

    var addButton: XCUIElement {
        let byID = app.buttons[AccessibilityIdentifiers.Document.addButton]
        if byID.exists { return byID }
        let navBarButtons = app.navigationBars.buttons
        for i in 0..<navBarButtons.count {
            let button = navBarButtons.element(boundBy: i)
            if button.label == "plus" || button.label.contains("Add") {
                if button.isEnabled { return button }
            }
        }
        return app.buttons.containing(NSPredicate(format: "label CONTAINS 'plus'")).firstMatch
    }

    var emptyState: XCUIElement {
        app.otherElements[AccessibilityIdentifiers.Document.emptyStateView]
    }

    var documentsList: XCUIElement {
        app.otherElements[AccessibilityIdentifiers.Document.documentsList]
    }

    func waitForLoad(timeout: TimeInterval = 15) {
        let deadline = Date().addingTimeInterval(timeout)
        var loaded = false
        repeat {
            loaded = addButton.exists
                || emptyState.exists
                || documentsList.exists
                || app.staticTexts.containing(NSPredicate(format: "label CONTAINS[c] 'Documents' OR label CONTAINS[c] 'Warranties'")).firstMatch.exists
            if loaded { break }
            RunLoop.current.run(until: Date().addingTimeInterval(0.2))
        } while Date() < deadline
        XCTAssertTrue(loaded, "Expected document list screen to load")
    }

    func openCreateDocument() {
        addButton.waitForExistenceOrFail(timeout: 10)
        addButton.forceTap()
    }

    func findDocument(title: String) -> XCUIElement {
        app.staticTexts.containing(NSPredicate(format: "label CONTAINS %@", title)).firstMatch
    }
}

/// Page object for the document create/edit form.
struct DocumentFormScreen {
    let app: XCUIApplication

    var titleField: XCUIElement {
        app.textFields[AccessibilityIdentifiers.Document.titleField]
    }

    var residencePicker: XCUIElement {
        app.buttons[AccessibilityIdentifiers.Document.residencePicker]
    }

    var saveButton: XCUIElement {
        app.buttons[AccessibilityIdentifiers.Document.saveButton]
    }

    var cancelButton: XCUIElement {
        app.buttons[AccessibilityIdentifiers.Document.formCancelButton]
    }

    func waitForLoad(timeout: TimeInterval = 15) {
        XCTAssertTrue(titleField.waitForExistence(timeout: timeout), "Expected document form to load")
    }

    func enterTitle(_ text: String) {
        titleField.waitForExistenceOrFail(timeout: 10)
        titleField.focusAndType(text, app: app)
    }

    /// Selects a residence by name from the picker. Returns true if selection succeeded.
    @discardableResult
    func selectResidence(name: String) -> Bool {
        guard residencePicker.waitForExistence(timeout: 5) else { return false }
        residencePicker.tap()

        let menuItem = app.menuItems.firstMatch
        if menuItem.waitForExistence(timeout: 5) {
            // Look for matching item first
            let matchingItem = app.menuItems.containing(NSPredicate(format: "label CONTAINS[c] %@", name)).firstMatch
            if matchingItem.exists {
                matchingItem.tap()
                return true
            }
            // Fallback: tap last available item
            let allItems = app.menuItems.allElementsBoundByIndex
            if let last = allItems.last {
                last.tap()
                return true
            }
        }
        // Dismiss picker if nothing found
        app.coordinate(withNormalizedOffset: CGVector(dx: 0.5, dy: 0.9)).tap()
        return false
    }

    func save() {
        KeyboardDismisser.dismiss(app: app)
        // Unconditional swipe-up matches the task form fix — forces SwiftUI
        // state to commit before the submit button reads it.
        app.swipeUp()
        saveButton.waitForExistenceOrFail(timeout: 10)
        if saveButton.isHittable {
            saveButton.tap()
        } else {
            saveButton.coordinate(withNormalizedOffset: CGVector(dx: 0.5, dy: 0.5)).tap()
        }
        _ = saveButton.waitForNonExistence(timeout: 15)
    }

    func cancel() {
        cancelButton.waitForExistenceOrFail(timeout: 10)
        cancelButton.forceTap()
    }
}

// MARK: - Residence Detail Screen

/// Page object for the residence detail screen.
struct ResidenceDetailScreen {
    let app: XCUIApplication

    var editButton: XCUIElement {
        app.buttons[AccessibilityIdentifiers.Residence.editButton]
    }

    var deleteButton: XCUIElement {
        app.buttons[AccessibilityIdentifiers.Residence.deleteButton]
    }

    var shareButton: XCUIElement {
        app.buttons[AccessibilityIdentifiers.Residence.shareButton]
    }

    func waitForLoad(timeout: TimeInterval = 15) {
        let deadline = Date().addingTimeInterval(timeout)
        var loaded = false
        repeat {
            loaded = editButton.exists
                || app.otherElements[AccessibilityIdentifiers.Residence.detailView].exists
                || app.staticTexts.containing(NSPredicate(format: "label CONTAINS[c] 'Tasks' OR label CONTAINS[c] 'Maintenance'")).firstMatch.exists
            if loaded { break }
            RunLoop.current.run(until: Date().addingTimeInterval(0.2))
        } while Date() < deadline
        XCTAssertTrue(loaded, "Expected residence detail screen to load")
    }

    func tapEdit() {
        editButton.waitForExistenceOrFail(timeout: 10)
        editButton.forceTap()
    }

    func tapDelete() {
        deleteButton.waitForExistenceOrFail(timeout: 10)
        deleteButton.forceTap()
    }

    func tapShare() {
        shareButton.waitForExistenceOrFail(timeout: 10)
        shareButton.forceTap()
    }
}
