import { render, screen, waitFor, within } from "@testing-library/react"
import userEvent from "@testing-library/user-event"
import { LoginPage } from "../pages/LoginPage"

jest.mock("sonner", () => ({
    toast: {
        error: jest.fn(),
        success: jest.fn(),
    },
}))

describe("LoginPage", () => {
    const adminEmail = "admin@royalenfield.com"
    const adminPassword = "Admin@123"

    beforeEach(() => {
        jest.clearAllMocks()
        window.alert = jest.fn()
    })

    function setup(onLogin = jest.fn().mockResolvedValue(undefined)) {
        render(<LoginPage onLogin={onLogin} />)
        return { onLogin }
    }

    it("renders branding and primary login controls", () => {
        setup()
        expect(
            screen.getByRole("heading", { name: /royal enfield/i }),
        ).toBeInTheDocument()
        expect(screen.getByLabelText(/^email address$/i)).toBeInTheDocument()
        expect(screen.getByLabelText(/^password$/i)).toBeInTheDocument()
        expect(screen.getByRole("button", { name: /^login$/i })).toBeEnabled()
        expect(
            screen.getByRole("button", { name: /prospective user login/i }),
        ).toBeInTheDocument()
        expect(screen.getByText(/test user credentials/i)).toBeInTheDocument()
    })

    it("shows validation error when email or password is missing", async () => {
        const user = userEvent.setup()
        setup()
        await user.click(screen.getByRole("button", { name: /^login$/i }))
        expect(
            await screen.findByText(/please enter both email and password/i),
        ).toBeInTheDocument()
    })

    it("calls onLogin with entered credentials on success", async () => {
        const user = userEvent.setup()
        const onLogin = jest.fn().mockResolvedValue(undefined)
        setup(onLogin)

        await user.type(
            screen.getByLabelText(/^email address$/i),
            adminEmail,
        )
        await user.type(
            screen.getByLabelText(/^password$/i),
            adminPassword,
        )
        await user.click(screen.getByRole("button", { name: /^login$/i }))

        await waitFor(() => {
            expect(onLogin).toHaveBeenCalledWith(adminEmail, adminPassword)
        })
    })

    it("shows server error message when onLogin rejects", async () => {
        const user = userEvent.setup()
        const onLogin = jest
            .fn()
            .mockRejectedValue({ message: "Invalid credentials" })
        setup(onLogin)

        await user.type(
            screen.getByLabelText(/^email address$/i),
            adminEmail,
        )
        await user.type(screen.getByLabelText(/^password$/i), "wrong-pass")
        await user.click(screen.getByRole("button", { name: /^login$/i }))

        expect(
            await screen.findByText(/invalid credentials/i),
        ).toBeInTheDocument()
    })

    it("toggles forgot-password view and returns with Back to Login", async () => {
        const user = userEvent.setup()
        setup()

        await user.click(
            screen.getByRole("button", { name: /forgot password/i }),
        )
        expect(
            screen.getByRole("heading", { name: /reset password/i }),
        ).toBeInTheDocument()
        expect(
            screen.queryByRole("button", { name: /^login$/i }),
        ).not.toBeInTheDocument()

        await user.click(
            screen.getByRole("button", { name: /back to login/i }),
        )
        expect(
            screen.getByRole("button", { name: /^login$/i }),
        ).toBeInTheDocument()
        expect(
            screen.queryByRole("heading", { name: /reset password/i }),
        ).not.toBeInTheDocument()
    })

    it("quick-login card triggers onLogin with mock user credentials", async () => {
        const user = userEvent.setup()
        const onLogin = jest.fn().mockResolvedValue(undefined)
        setup(onLogin)

        await user.click(
            screen.getByText(/click to login as super admin/i),
        )

        await waitFor(() => {
            expect(onLogin).toHaveBeenCalledWith(adminEmail, adminPassword)
        })
    })

    it("shows loading state on submit while onLogin is pending", async () => {
        const user = userEvent.setup()
        let resolveLogin!: () => void
        const pending = new Promise<void>((res) => {
            resolveLogin = res
        })
        const onLogin = jest.fn().mockReturnValue(pending)
        setup(onLogin)

        await user.type(
            screen.getByLabelText(/^email address$/i),
            adminEmail,
        )
        await user.type(
            screen.getByLabelText(/^password$/i),
            adminPassword,
        )
        await user.click(screen.getByRole("button", { name: /^login$/i }))

        expect(screen.getByText(/logging in/i)).toBeInTheDocument()
        resolveLogin()
        await waitFor(() => {
            expect(screen.queryByText(/logging in/i)).not.toBeInTheDocument()
        })
    })

    it("toggles password visibility", async () => {
        const user = userEvent.setup()
        setup()
        const passwordInput = screen.getByLabelText(
            /^password$/i,
        ) as HTMLInputElement
        expect(passwordInput.type).toBe("password")

        const pwdContainer = passwordInput.closest(".relative")
        const toggleBtn = pwdContainer
            ? within(pwdContainer as HTMLElement).getAllByRole("button")[0]
            : null
        expect(toggleBtn).toBeTruthy()
        await user.click(toggleBtn as HTMLElement)

        expect((screen.getByLabelText(/^password$/i) as HTMLInputElement).type).toBe(
            "text",
        )
    })
})
