import { describe, it } from "@effect/vitest"
import { Cause, Redacted, Result } from "effect"

import { Url, UrlParams } from "effect/unstable/http"
import {
  assertFailure,
  assertInstanceOf,
  assertSuccess,
  assertTrue,
  deepStrictEqual,
  strictEqual
} from "../../utils/assert.ts"

describe("Url", () => {
  const testURL = new URL("https://example.com/test")

  const expectUrl = (updatedUrl: URL, expected: string) => {
    assertTrue(updatedUrl !== testURL)
    assertTrue(updatedUrl.toString() !== testURL.toString())
    strictEqual(updatedUrl.toString(), expected)
  }

  describe("make", () => {
    it("appends query parameters and hash", () => {
      assertSuccess(
        Url.make(
          "https://example.com/test?existing=true",
          UrlParams.fromInput([["foo", "bar"], ["foo", "baz"]]),
          "section"
        ),
        new URL("https://example.com/test?existing=true&foo=bar&foo=baz#section")
      )
    })

    it("fails when the URL cannot be constructed", () => {
      const result = Url.make("http://%", UrlParams.empty, undefined)

      assertTrue(Result.isFailure(result))
      assertInstanceOf(result.failure, Url.UrlError)
    })
  })

  describe("fromString", () => {
    it("parses absolute URLs", () => {
      const url = Url.fromString(testURL.toString())
      assertSuccess(url, testURL)
    })

    it("resolves relative URLs against a base URL", () => {
      const error = Url.fromString("??")
      assertFailure(error, new Cause.IllegalArgumentError("Invalid URL: \"??\""))
    })
  })

  it("mutate", () => {
    expectUrl(
      Url.mutate(testURL, (url) => {
        url.username = "user"
        url.password = "pass"
      }),
      "https://user:pass@example.com/test"
    )
  })

  it("setHash", () => {
    expectUrl(Url.setHash(testURL, "test"), "https://example.com/test#test")
  })

  it("setHost", () => {
    expectUrl(Url.setHost(testURL, "newhost.com"), "https://newhost.com/test")
  })

  it("setHostname", () => {
    expectUrl(Url.setHostname(testURL, "newhostname.com"), "https://newhostname.com/test")
  })

  it("setHref", () => {
    expectUrl(Url.setHref(testURL, "https://newhref.com"), "https://newhref.com/")
  })

  it("setPassword", () => {
    expectUrl(Url.setPassword(testURL, "newpassword"), "https://:newpassword@example.com/test")
  })

  it("setPassword - Redacted", () => {
    expectUrl(Url.setPassword(testURL, Redacted.make("newpassword")), "https://:newpassword@example.com/test")
  })

  it("setPathname", () => {
    expectUrl(Url.setPathname(testURL, "/newpath"), "https://example.com/newpath")
  })

  it("setPort", () => {
    expectUrl(Url.setPort(testURL, "8080"), "https://example.com:8080/test")
    expectUrl(Url.setPort(testURL, 8080), "https://example.com:8080/test")
  })

  it("setProtocol", () => {
    expectUrl(Url.setProtocol(testURL, "http"), "http://example.com/test")
  })

  it("setSearch", () => {
    expectUrl(Url.setSearch(testURL, "?key=value"), "https://example.com/test?key=value")
  })

  it("setUsername", () => {
    expectUrl(Url.setUsername(testURL, "newuser"), "https://newuser@example.com/test")
  })

  it("modifyUrlParams", () => {
    expectUrl(Url.modifyUrlParams(testURL, UrlParams.append("key", "value")), "https://example.com/test?key=value")
  })

  it("urlParams", () => {
    const params = Url.urlParams(new URL("https://example.com?foo=bar&baz=qux"))
    deepStrictEqual(params, UrlParams.fromInput([["foo", "bar"], ["baz", "qux"]]))
  })

  it("setUrlParams", () => {
    const url = new URL("https://example.com/?foo=bar&a=b")
    const newParams = UrlParams.fromInput([["foo", "bar2"], ["baz", "qux"]])
    const updatedUrl = Url.setUrlParams(url, newParams)
    expectUrl(updatedUrl, "https://example.com/?foo=bar2&baz=qux")
  })
})
