import XCTest

struct UITestID {
    struct Root {
        static let ready = "ui.app.ready"
        static let onboarding = "ui.root.onboarding"
        static let login = "ui.root.login"
        static let mainTabs = "ui.root.mainTabs"
    }

    struct Onboarding {
        static let welcomeTitle = "Onboarding.WelcomeTitle"
        static let startFreshButton = "Onboarding.StartFreshButton"
        static let joinExistingButton = "Onboarding.JoinExistingButton"
        static let loginButton = "Onboarding.LoginButton"
        static let valuePropsContainer = "Onboarding.ValuePropsTitle"
        static let valuePropsNextButton = "Onboarding.ValuePropsNextButton"
        static let nameResidenceTitle = "Onboarding.NameResidenceTitle"
        static let residenceNameField = "Onboarding.ResidenceNameField"
        static let nameResidenceContinueButton = "Onboarding.NameResidenceContinueButton"
        static let createAccountTitle = "Onboarding.CreateAccountTitle"
        static let emailSignUpExpandButton = "Onboarding.EmailSignUpExpandButton"
        static let createAccountButton = "Onboarding.CreateAccountButton"
        static let backButton = "Onboarding.BackButton"
        static let skipButton = "Onboarding.SkipButton"
        static let progressIndicator = "Onboarding.ProgressIndicator"
    }

    struct PasswordReset {
        static let emailField = "PasswordReset.EmailField"
        static let sendCodeButton = "PasswordReset.SendCodeButton"
        static let backToLoginButton = "PasswordReset.BackToLoginButton"
        static let codeField = "PasswordReset.CodeField"
        static let verifyCodeButton = "PasswordReset.VerifyCodeButton"
        static let resendCodeButton = "PasswordReset.ResendCodeButton"
        static let newPasswordField = "PasswordReset.NewPasswordField"
        static let confirmPasswordField = "PasswordReset.ConfirmPasswordField"
        static let resetButton = "PasswordReset.ResetButton"
        static let returnToLoginButton = "PasswordReset.ReturnToLoginButton"
    }

    struct Auth {
        static let usernameField = "Login.UsernameField"
        static let passwordField = "Login.PasswordField"
        static let passwordVisibilityToggle = "Login.PasswordVisibilityToggle"
        static let loginButton = "Login.LoginButton"
        static let signUpButton = "Login.SignUpButton"
        static let forgotPasswordButton = "Login.ForgotPasswordButton"

        static let registerUsernameField = "Register.UsernameField"
        static let registerEmailField = "Register.EmailField"
        static let registerPasswordField = "Register.PasswordField"
        static let registerConfirmPasswordField = "Register.ConfirmPasswordField"
        static let registerButton = "Register.RegisterButton"
        static let registerCancelButton = "Register.CancelButton"
    }
}

struct RootScreen {
    let app: XCUIApplication

    func waitForReady(timeout: TimeInterval = 15) {
        app.otherElements[UITestID.Root.ready].waitForExistenceOrFail(timeout: timeout)
    }
}

struct OnboardingWelcomeScreen {
    let app: XCUIApplication

    private var onboardingRoot: XCUIElement { app.otherElements[UITestID.Root.onboarding] }
    private var startFreshButton: XCUIElement { app.descendants(matching: .any).matching(identifier: UITestID.Onboarding.startFreshButton).firstMatch }
    private var joinExistingButton: XCUIElement { app.descendants(matching: .any).matching(identifier: UITestID.Onboarding.joinExistingButton).firstMatch }
    private var loginButton: XCUIElement { app.descendants(matching: .any).matching(identifier: UITestID.Onboarding.loginButton).firstMatch }
    private var backButton: XCUIElement { app.descendants(matching: .any).matching(identifier: UITestID.Onboarding.backButton).firstMatch }

    func waitForLoad(timeout: TimeInterval = 15) {
        onboardingRoot.waitForExistenceOrFail(timeout: timeout)
        if startFreshButton.waitForExistence(timeout: 2) {
            return
        }

        for _ in 0..<4 {
            if backButton.exists && backButton.isHittable {
                backButton.tap()
            }
            if startFreshButton.waitForExistence(timeout: 2) {
                return
            }
        }

        if !startFreshButton.waitForExistence(timeout: timeout) {
            XCTFail("Expected onboarding welcome entry point. Debug tree:\n\(app.debugDescription)")
        }
    }

    func tapStartFresh() {
        startFreshButton.waitUntilHittable(timeout: 10).tap()
    }

    func tapJoinExisting() {
        joinExistingButton.waitUntilHittable(timeout: 10).tap()
    }

    func tapAlreadyHaveAccount() {
        loginButton.waitForExistenceOrFail(timeout: 10)
        if loginButton.isHittable {
            loginButton.tap()
        } else {
            loginButton.forceTap()
        }
    }
}

struct OnboardingValuePropsScreen {
    let app: XCUIApplication

    private var container: XCUIElement { app.descendants(matching: .any).matching(identifier: UITestID.Onboarding.valuePropsContainer).firstMatch }
    private var continueButton: XCUIElement { app.descendants(matching: .any).matching(identifier: UITestID.Onboarding.valuePropsNextButton).firstMatch }
    private var backButton: XCUIElement { app.descendants(matching: .any).matching(identifier: UITestID.Onboarding.backButton).firstMatch }

    func waitForLoad(timeout: TimeInterval = 15) {
        container.waitForExistenceOrFail(timeout: timeout)
    }

    func tapContinue() {
        continueButton.waitUntilHittable(timeout: 10).tap()
    }

    func tapBack() {
        backButton.waitForExistenceOrFail(timeout: 10)
        backButton.forceTap()
    }
}

struct OnboardingNameResidenceScreen {
    let app: XCUIApplication

    private var title: XCUIElement { app.descendants(matching: .any).matching(identifier: UITestID.Onboarding.nameResidenceTitle).firstMatch }
    private var nameField: XCUIElement { app.textFields[UITestID.Onboarding.residenceNameField] }
    private var continueButton: XCUIElement { app.descendants(matching: .any).matching(identifier: UITestID.Onboarding.nameResidenceContinueButton).firstMatch }
    private var backButton: XCUIElement { app.descendants(matching: .any).matching(identifier: UITestID.Onboarding.backButton).firstMatch }

    func waitForLoad(timeout: TimeInterval = 15) {
        title.waitForExistenceOrFail(timeout: timeout)
    }

    func enterResidenceName(_ value: String) {
        nameField.waitUntilHittable(timeout: 10)
        nameField.focusAndType(value, app: app)
    }

    func tapContinue() {
        continueButton.waitUntilHittable(timeout: 10).tap()
    }

    func tapBack() {
        backButton.waitForExistenceOrFail(timeout: 10)
        backButton.forceTap()
    }
}

struct OnboardingCreateAccountScreen {
    let app: XCUIApplication

    private var title: XCUIElement { app.descendants(matching: .any).matching(identifier: UITestID.Onboarding.createAccountTitle).firstMatch }
    private var expandEmailButton: XCUIElement { app.descendants(matching: .any).matching(identifier: UITestID.Onboarding.emailSignUpExpandButton).firstMatch }
    private var createAccountButton: XCUIElement { app.descendants(matching: .any).matching(identifier: UITestID.Onboarding.createAccountButton).firstMatch }

    func waitForLoad(timeout: TimeInterval = 15) {
        title.waitForExistenceOrFail(timeout: timeout)
    }

    func expandEmailSignup() {
        expandEmailButton.waitUntilHittable(timeout: 10).tap()
    }

    func waitForCreateAccountButton(timeout: TimeInterval = 10) {
        createAccountButton.waitForExistenceOrFail(timeout: timeout)
    }
}

struct LoginScreenObject {
    let app: XCUIApplication

    private var usernameField: XCUIElement { app.textFields[UITestID.Auth.usernameField] }
    private var passwordSecureField: XCUIElement { app.secureTextFields[UITestID.Auth.passwordField] }
    private var passwordVisibleField: XCUIElement { app.textFields[UITestID.Auth.passwordField] }
    private var loginButton: XCUIElement { app.buttons[UITestID.Auth.loginButton] }
    private var signUpButton: XCUIElement { app.buttons[UITestID.Auth.signUpButton] }
    private var forgotPasswordButton: XCUIElement { app.buttons[UITestID.Auth.forgotPasswordButton] }
    private var visibilityToggle: XCUIElement { app.buttons[UITestID.Auth.passwordVisibilityToggle] }

    func waitForLoad(timeout: TimeInterval = 15) {
        usernameField.waitForExistenceOrFail(timeout: timeout)
        loginButton.waitForExistenceOrFail(timeout: timeout)
    }

    func enterUsername(_ username: String) {
        usernameField.waitUntilHittable(timeout: 10)
        usernameField.focusAndType(username, app: app)
    }

    func enterPassword(_ password: String) {
        if passwordSecureField.exists {
            passwordSecureField.focusAndType(password, app: app)
        } else {
            passwordVisibleField.waitUntilHittable(timeout: 10)
            passwordVisibleField.focusAndType(password, app: app)
        }
    }

    func tapPasswordVisibilityToggle() {
        visibilityToggle.waitUntilHittable(timeout: 10).tap()
    }

    func tapSignUp() {
        signUpButton.waitForExistenceOrFail(timeout: 10)
        if !signUpButton.isHittable {
            let scrollView = app.scrollViews.firstMatch
            if scrollView.exists {
                signUpButton.scrollIntoView(in: scrollView)
            }
        }
        signUpButton.forceTap()
    }

    func tapForgotPassword() {
        forgotPasswordButton.waitForExistenceOrFail(timeout: 10)
        if !forgotPasswordButton.isHittable {
            // Dismiss keyboard if it's covering the button
            if app.keyboards.firstMatch.exists {
                let navBar = app.navigationBars.firstMatch
                if navBar.exists { navBar.tap() }
                _ = app.keyboards.firstMatch.waitForNonExistence(timeout: 2)
            }
            let scrollView = app.scrollViews.firstMatch
            if scrollView.exists && !forgotPasswordButton.isHittable {
                forgotPasswordButton.scrollIntoView(in: scrollView)
            }
        }
        forgotPasswordButton.forceTap()
    }

    func assertPasswordFieldVisible() {
        // After toggling visibility, SwiftUI may expose the field as either
        // a regular textField or keep it as a secureTextField depending on
        // the accessibility tree update timing. Accept either element type
        // as proof that the password control is still operable.
        let visibleExists = passwordVisibleField.waitForExistence(timeout: 5)
        let secureExists = !visibleExists && passwordSecureField.waitForExistence(timeout: 2)
        XCTAssertTrue(visibleExists || secureExists, "Expected password field (secure or plain) to remain operable after toggle")
    }
}

struct RegisterScreenObject {
    let app: XCUIApplication

    private var usernameField: XCUIElement { app.textFields[UITestID.Auth.registerUsernameField] }
    private var emailField: XCUIElement { app.textFields[UITestID.Auth.registerEmailField] }
    private var registerButton: XCUIElement { app.buttons[UITestID.Auth.registerButton] }
    private var cancelButton: XCUIElement { app.buttons[UITestID.Auth.registerCancelButton] }

    func waitForLoad(timeout: TimeInterval = 15) {
        usernameField.waitForExistenceOrFail(timeout: timeout)
        registerButton.waitForExistenceOrFail(timeout: timeout)
    }

    func fill(username: String, email: String, password: String) {
        // iOS 26 bug: SecureTextField won't gain keyboard focus when tapped directly.
        // Workaround: toggle password visibility first to convert SecureField → TextField,
        // then use focusAndType() on all regular TextFields.
        usernameField.waitForExistenceOrFail(timeout: 10)

        // Scroll down to reveal the password toggle buttons (they're below the fold)
        let scrollView = app.scrollViews.firstMatch
        if scrollView.exists { scrollView.swipeUp() }

        // Toggle both password visibility buttons (converts SecureField → TextField)
        let toggleButtons = app.buttons.matching(NSPredicate(format: "label == 'Toggle password visibility'"))
        for i in 0..<toggleButtons.count {
            let toggle = toggleButtons.element(boundBy: i)
            if toggle.exists && toggle.isHittable {
                toggle.tap()
            }
        }

        // After toggling, password fields are regular TextFields.
        // Don't swipeDown — it dismisses the sheet. focusAndType() auto-scrolls via tap().
        let passwordField = app.textFields[UITestID.Auth.registerPasswordField]
        let confirmPasswordField = app.textFields[UITestID.Auth.registerConfirmPasswordField]

        usernameField.focusAndType(username, app: app)
        emailField.focusAndType(email, app: app)
        passwordField.focusAndType(password, app: app)
        confirmPasswordField.focusAndType(password, app: app)
    }

    func tapCancel() {
        cancelButton.waitUntilHittable(timeout: 10).tap()
    }
}

// MARK: - Password Reset Screens

struct ForgotPasswordScreen {
    let app: XCUIApplication

    private var emailField: XCUIElement { app.textFields[UITestID.PasswordReset.emailField] }
    private var sendCodeButton: XCUIElement { app.buttons[UITestID.PasswordReset.sendCodeButton] }
    private var backToLoginButton: XCUIElement { app.buttons[UITestID.PasswordReset.backToLoginButton] }

    func waitForLoad(timeout: TimeInterval = 15) {
        // Wait for the email field or the "Forgot Password?" title
        let emailLoaded = emailField.waitForExistence(timeout: timeout)
        if !emailLoaded {
            let title = app.staticTexts.containing(
                NSPredicate(format: "label CONTAINS[c] 'Forgot Password'")
            ).firstMatch
            XCTAssertTrue(title.waitForExistence(timeout: 5), "Expected forgot password screen to load")
        }
    }

    func enterEmail(_ email: String) {
        emailField.waitUntilHittable(timeout: 10)
        emailField.focusAndType(email, app: app)
    }

    func tapSendCode() {
        sendCodeButton.waitForExistenceOrFail(timeout: 10)
        sendCodeButton.forceTap()
    }

    func tapBackToLogin() {
        backToLoginButton.waitUntilHittable(timeout: 10).tap()
    }
}

struct VerifyResetCodeScreen {
    let app: XCUIApplication

    private var codeField: XCUIElement { app.textFields[UITestID.PasswordReset.codeField] }
    private var verifyCodeButton: XCUIElement { app.buttons[UITestID.PasswordReset.verifyCodeButton] }
    private var resendCodeButton: XCUIElement { app.buttons[UITestID.PasswordReset.resendCodeButton] }

    func waitForLoad(timeout: TimeInterval = 15) {
        let codeLoaded = codeField.waitForExistence(timeout: timeout)
        if !codeLoaded {
            let title = app.staticTexts.containing(
                NSPredicate(format: "label CONTAINS[c] 'Check Your Email'")
            ).firstMatch
            XCTAssertTrue(title.waitForExistence(timeout: 5), "Expected verify reset code screen to load")
        }
    }

    func enterCode(_ code: String) {
        codeField.waitUntilHittable(timeout: 10)
        codeField.focusAndType(code, app: app)
    }

    func tapVerify() {
        verifyCodeButton.waitForExistenceOrFail(timeout: 10)
        verifyCodeButton.forceTap()
    }

    func tapResendCode() {
        resendCodeButton.waitUntilHittable(timeout: 10).tap()
    }
}

struct ResetPasswordScreen {
    let app: XCUIApplication

    // The new password field may be a SecureField or TextField depending on visibility toggle
    private var newPasswordSecureField: XCUIElement { app.secureTextFields[UITestID.PasswordReset.newPasswordField] }
    private var newPasswordVisibleField: XCUIElement { app.textFields[UITestID.PasswordReset.newPasswordField] }
    private var confirmPasswordSecureField: XCUIElement { app.secureTextFields[UITestID.PasswordReset.confirmPasswordField] }
    private var confirmPasswordVisibleField: XCUIElement { app.textFields[UITestID.PasswordReset.confirmPasswordField] }
    private var resetButton: XCUIElement { app.buttons[UITestID.PasswordReset.resetButton] }
    private var returnToLoginButton: XCUIElement { app.buttons[UITestID.PasswordReset.returnToLoginButton] }

    func waitForLoad(timeout: TimeInterval = 15) throws {
        let loaded = newPasswordSecureField.waitForExistence(timeout: timeout)
            || newPasswordVisibleField.waitForExistence(timeout: 3)
        if !loaded {
            let title = app.staticTexts.containing(
                NSPredicate(format: "label CONTAINS[c] 'Set New Password'")
            ).firstMatch
            if !title.waitForExistence(timeout: 5) {
                throw XCTSkip("Reset password screen did not load — verify code step may have failed")
            }
        }
    }

    func enterNewPassword(_ password: String) {
        if newPasswordSecureField.exists {
            newPasswordSecureField.waitUntilHittable(timeout: 10)
            newPasswordSecureField.focusAndType(password, app: app)
        } else {
            newPasswordVisibleField.waitUntilHittable(timeout: 10)
            newPasswordVisibleField.focusAndType(password, app: app)
        }
    }

    func enterConfirmPassword(_ password: String) {
        if confirmPasswordSecureField.exists {
            confirmPasswordSecureField.waitUntilHittable(timeout: 10)
            confirmPasswordSecureField.focusAndType(password, app: app)
        } else {
            confirmPasswordVisibleField.waitUntilHittable(timeout: 10)
            confirmPasswordVisibleField.focusAndType(password, app: app)
        }
    }

    func tapReset() {
        resetButton.waitForExistenceOrFail(timeout: 10)
        XCTAssertTrue(resetButton.isEnabled,
            "Reset button should be enabled — if disabled, password fields likely have mismatched values from iOS strong password autofill")
        resetButton.forceTap()
    }

    func tapReturnToLogin() {
        returnToLoginButton.waitUntilHittable(timeout: 10).tap()
    }

    var isResetButtonEnabled: Bool {
        resetButton.waitForExistenceOrFail(timeout: 10)
        return resetButton.isEnabled
    }
}
