import XCTest

/// Tests for the password reset flow against the local backend (DEBUG=true, code=123456).
///
/// Test Plan IDs: AUTH-015, AUTH-016, AUTH-017
final class PasswordResetTests: BaseUITestCase {
    override var relaunchBetweenTests: Bool { true }

    private var testSession: TestSession?
    private var cleaner: TestDataCleaner?

    override func setUpWithError() throws {
        guard TestAccountAPIClient.isBackendReachable() else {
            throw XCTSkip("Local backend is not reachable at \(TestAccountAPIClient.baseURL)")
        }

        guard let session = TestAccountManager.createVerifiedAccount() else {
            throw XCTSkip("Could not create verified test account")
        }
        testSession = session
        cleaner = TestDataCleaner(token: session.token)

        // Force clean app launch — password reset flow leaves complex screen state
        app.terminate()
        try super.setUpWithError()
    }

    override func tearDownWithError() throws {
        cleaner?.cleanAll()
        try super.tearDownWithError()
    }

    // MARK: - AUTH-015: Verify reset code reaches new password screen

    func testAUTH015_VerifyResetCodeSuccessPath() throws {
        let session = try XCTUnwrap(testSession)

        // Navigate to forgot password
        let login = TestFlows.navigateToLoginFromOnboarding(app: app)
        login.tapForgotPassword()

        // Enter email and send code
        let forgotScreen = ForgotPasswordScreen(app: app)
        forgotScreen.waitForLoad()
        forgotScreen.enterEmail(session.user.email)
        forgotScreen.tapSendCode()

        // Enter the debug verification code
        let verifyScreen = VerifyResetCodeScreen(app: app)
        verifyScreen.waitForLoad()
        verifyScreen.enterCode(TestAccountAPIClient.debugVerificationCode)
        verifyScreen.tapVerify()

        // Should reach the new password screen
        let resetScreen = ResetPasswordScreen(app: app)
        try resetScreen.waitForLoad(timeout: loginTimeout)
    }

    // MARK: - AUTH-016: Full reset password cycle + login with new password

    func testAUTH016_ResetPasswordSuccess() throws {
        let session = try XCTUnwrap(testSession)
        let newPassword = "NewPass9876!"

        // Navigate to forgot password
        let login = TestFlows.navigateToLoginFromOnboarding(app: app)
        login.tapForgotPassword()

        // Complete the full reset flow via UI
        try TestFlows.completeForgotPasswordFlow(
            app: app,
            email: session.user.email,
            newPassword: newPassword
        )

        // After reset, the app auto-logs in with the new password.
        // If auto-login succeeds → app goes directly to main tabs (sheet dismissed).
        // If auto-login fails → success message + "Return to Login" button appear.
        let tabBar = app.tabBars.firstMatch
        let returnButton = app.buttons[UITestID.PasswordReset.returnToLoginButton]

        let deadline = Date().addingTimeInterval(loginTimeout)
        var reachedPostReset = false
        while Date() < deadline {
            if tabBar.exists {
                // Auto-login succeeded — password reset worked!
                reachedPostReset = true
                break
            }
            if returnButton.exists {
                // Auto-login failed — manual login needed
                reachedPostReset = true
                returnButton.forceTap()
                break
            }
            RunLoop.current.run(until: Date().addingTimeInterval(0.5))
        }

        XCTAssertTrue(reachedPostReset, "Expected main tabs (auto-login) or return button (manual login) after password reset")

        if tabBar.exists {
            // Already logged in via auto-login — test passed
            return
        }

        // Manual login path: return button was tapped, now on login screen
        let loginScreen = LoginScreenObject(app: app)
        loginScreen.waitForLoad(timeout: loginTimeout)
        loginScreen.enterUsername(session.username)
        loginScreen.enterPassword(newPassword)

        let loginButton = app.buttons[UITestID.Auth.loginButton]
        loginButton.waitUntilHittable(timeout: 10).tap()

        XCTAssertTrue(tabBar.waitForExistence(timeout: loginTimeout), "Should login successfully with new password")
    }

    // MARK: - AUTH-015 (alias): Verify reset code reaches the new password screen

    func test03_verifyResetCodeSuccess() throws {
        try XCTSkipIf(!TestAccountAPIClient.isBackendReachable(), "Backend not reachable")

        let session = try XCTUnwrap(testSession)

        // Navigate to forgot password
        let login = TestFlows.navigateToLoginFromOnboarding(app: app)
        login.tapForgotPassword()

        // Enter email and send the reset code
        let forgotScreen = ForgotPasswordScreen(app: app)
        forgotScreen.waitForLoad()
        forgotScreen.enterEmail(session.user.email)
        forgotScreen.tapSendCode()

        // Enter the debug verification code on the verify screen
        let verifyScreen = VerifyResetCodeScreen(app: app)
        verifyScreen.waitForLoad()
        verifyScreen.enterCode(TestAccountAPIClient.debugVerificationCode)
        verifyScreen.tapVerify()

        // The reset password screen should now appear
        let resetScreen = ResetPasswordScreen(app: app)
        try resetScreen.waitForLoad(timeout: loginTimeout)
    }

    // MARK: - AUTH-016 (alias): Full reset flow + login with new password

    func test04_resetPasswordSuccessAndLogin() throws {
        try XCTSkipIf(!TestAccountAPIClient.isBackendReachable(), "Backend not reachable")

        let session = try XCTUnwrap(testSession)
        let newPassword = "NewPass9876!"

        // Navigate to forgot password, then drive the complete 3-step reset flow
        let login = TestFlows.navigateToLoginFromOnboarding(app: app)
        login.tapForgotPassword()

        try TestFlows.completeForgotPasswordFlow(
            app: app,
            email: session.user.email,
            newPassword: newPassword
        )

        // Wait for a success indication — either a success message or the return-to-login button
        let successText = app.staticTexts.containing(
            NSPredicate(format: "label CONTAINS[c] 'success' OR label CONTAINS[c] 'reset'")
        ).firstMatch
        let returnButton = app.buttons[UITestID.PasswordReset.returnToLoginButton]

        // After reset, the app auto-logs in with the new password.
        // If auto-login succeeds → app goes to main tabs. If fails → return button appears.
        let tabBar = app.tabBars.firstMatch

        let deadline = Date().addingTimeInterval(loginTimeout)
        var reachedPostReset = false
        while Date() < deadline {
            if tabBar.exists {
                reachedPostReset = true
                break
            }
            if returnButton.exists {
                reachedPostReset = true
                returnButton.forceTap()
                break
            }
            RunLoop.current.run(until: Date().addingTimeInterval(0.5))
        }

        XCTAssertTrue(reachedPostReset, "Expected main tabs (auto-login) or return button after password reset")

        if tabBar.exists { return }

        // Manual login fallback
        let loginScreen = LoginScreenObject(app: app)
        loginScreen.waitForLoad(timeout: loginTimeout)
        loginScreen.enterUsername(session.username)
        loginScreen.enterPassword(newPassword)

        let loginButton = app.buttons[UITestID.Auth.loginButton]
        loginButton.waitUntilHittable(timeout: 10).tap()

        XCTAssertTrue(tabBar.waitForExistence(timeout: loginTimeout), "Should login successfully with new password")
    }

    // MARK: - AUTH-017: Mismatched passwords are blocked

    func testAUTH017_MismatchedPasswordBlocked() throws {
        let session = try XCTUnwrap(testSession)

        // Navigate to forgot password
        let login = TestFlows.navigateToLoginFromOnboarding(app: app)
        login.tapForgotPassword()

        // Get to the reset password screen
        let forgotScreen = ForgotPasswordScreen(app: app)
        forgotScreen.waitForLoad()
        forgotScreen.enterEmail(session.user.email)
        forgotScreen.tapSendCode()

        let verifyScreen = VerifyResetCodeScreen(app: app)
        verifyScreen.waitForLoad()
        verifyScreen.enterCode(TestAccountAPIClient.debugVerificationCode)
        verifyScreen.tapVerify()

        // Enter mismatched passwords
        let resetScreen = ResetPasswordScreen(app: app)
        try resetScreen.waitForLoad(timeout: loginTimeout)
        resetScreen.enterNewPassword("ValidPass123!")
        resetScreen.enterConfirmPassword("DifferentPass456!")

        // The reset button should be disabled when passwords don't match
        XCTAssertFalse(resetScreen.isResetButtonEnabled, "Reset button should be disabled when passwords don't match")
    }
}
