import { render } from "@opensea/testing-utils/render"
import { Tooltip } from "@opensea/ui-kit"
import { act, screen, waitFor } from "@testing-library/react"
import { userEvent } from "@testing-library/user-event"
import React from "react"
import { beforeEach, describe, expect, test, vi } from "vitest"
import { AppRouter } from "../../tests/AppRouter"
import { Link } from "./Link"

describe("<Link />", () => {
  const windowOpenSpy = vi
    .spyOn(window, "open")
    .mockImplementation(function noop() {
      return null
    })

  beforeEach(() => {
    Object.defineProperty(window, "location", {
      writable: true,
      value: { ...window.location, assign: vi.fn(), open: windowOpenSpy },
    })
  })

  test("absolute", () => {
    render(<Link href="https://opensea.io">Click me</Link>, {
      wrapper: ({ children }) => <AppRouter>{children}</AppRouter>,
    })
    const link = screen.getByRole("link", { name: "Click me" })
    expect(link).toHaveAttribute("href", "https://opensea.io/")
  })

  test("nested", async () => {
    const nestedClickHandler = vi.fn()
    render(
      <Link href="https://opensea.io">
        <div>
          <Link href="https://google.com" onClick={nestedClickHandler}>
            Click me
          </Link>
        </div>
      </Link>,
      { wrapper: ({ children }) => <AppRouter>{children}</AppRouter> },
    )

    const [outerLink, innerLink] = screen.getAllByRole("link", {
      name: "Click me",
    })

    expect(outerLink).toHaveAttribute("href", "https://opensea.io/")
    expect(outerLink.nodeName).toEqual("A")
    expect(innerLink.nodeName).toEqual("SPAN")
    await userEvent.click(innerLink)
    expect(nestedClickHandler).toHaveBeenCalled()
    expect(windowOpenSpy).toHaveBeenCalledWith("https://google.com/", "_blank")
  })

  test("url object - external", () => {
    render(
      <Link href={{ pathname: "https://opensea.io", query: { bar: "baz" } }}>
        Click me
      </Link>,
      { wrapper: ({ children }) => <AppRouter>{children}</AppRouter> },
    )

    const link = screen.getByRole("link", { name: "Click me" })
    expect(link).toHaveAttribute("href", "https://opensea.io/?bar=baz")
  })

  test("url object", () => {
    render(
      <Link href={{ pathname: "/foo", query: { bar: "baz" } }}>Click me</Link>,
      { wrapper: ({ children }) => <AppRouter>{children}</AppRouter> },
    )

    const link = screen.getByRole("link", { name: "Click me" })
    expect(link).toHaveAttribute("href", "/foo?bar=baz")
  })

  describe("should forward ref", () => {
    test("external link", async () => {
      render(
        <Tooltip content="Hello world!">
          <Link href="https://opensea.io">Click me</Link>
        </Tooltip>,
        {
          wrapper: ({ children }) => <AppRouter>{children}</AppRouter>,
        },
      )

      expect(
        screen.queryByRole("tooltip", { name: "Hello world!" }),
      ).not.toBeInTheDocument()

      await act(async () => {
        await userEvent.hover(screen.getByRole("link", { name: "Click me" }))
      })

      await screen.findByRole("tooltip", { name: "Hello world!" })

      expect(
        screen.getByRole("tooltip", { name: "Hello world!" }),
      ).toBeInTheDocument()
    })

    test("internal link", async () => {
      render(
        <Tooltip content="Hello world!">
          <Link href="/">Click me</Link>
        </Tooltip>,
        {
          wrapper: ({ children }) => <AppRouter>{children}</AppRouter>,
        },
      )

      expect(
        screen.queryByRole("tooltip", { name: "Hello world!" }),
      ).not.toBeInTheDocument()

      await act(async () => {
        await userEvent.hover(screen.getByRole("link", { name: "Click me" }))
      })

      await screen.findByRole("tooltip", { name: "Hello world!" })

      expect(
        screen.getByRole("tooltip", { name: "Hello world!" }),
      ).toBeInTheDocument()
    })
  })

  test("onNavigationCompleted", async () => {
    const onNavigationCompleted = vi.fn()
    render(
      <Link href="https://opensea.io">
        <Link
          href="/collection/azuki"
          onNavigationCompleted={onNavigationCompleted}
        >
          Click me
        </Link>
      </Link>,
      { wrapper: ({ children }) => <AppRouter>{children}</AppRouter> },
    )

    const [_, innerLink] = screen.getAllByRole("link", {
      name: "Click me",
    })

    await act(async () => {
      await userEvent.click(innerLink)
    })

    await waitFor(() => {
      const args = onNavigationCompleted.mock.calls[0]
      expect(args[0]).toBeTypeOf("number")
      expect(args[0]).toBeGreaterThan(0)
      expect(args[1]).toBeTypeOf("number")
      expect(args[1]).toBeGreaterThan(0)
    })
  })
})
