defmodule Towerops.EctoTypes.JsonAnyTest do
  use ExUnit.Case, async: true

  alias Towerops.EctoTypes.JsonAny

  describe "type/0" do
    test "returns :jsonb" do
      assert JsonAny.type() == :jsonb
    end
  end

  describe "cast/1" do
    test "casts nil" do
      assert JsonAny.cast(nil) == {:ok, nil}
    end

    test "casts strings" do
      assert JsonAny.cast("hello") == {:ok, "hello"}
      assert JsonAny.cast("") == {:ok, ""}
      assert JsonAny.cast("with spaces") == {:ok, "with spaces"}
    end

    test "casts integers" do
      assert JsonAny.cast(0) == {:ok, 0}
      assert JsonAny.cast(42) == {:ok, 42}
      assert JsonAny.cast(-100) == {:ok, -100}
    end

    test "casts floats" do
      assert JsonAny.cast(0.0) == {:ok, 0.0}
      assert JsonAny.cast(3.14) == {:ok, 3.14}
      assert JsonAny.cast(-99.99) == {:ok, -99.99}
    end

    test "casts booleans" do
      assert JsonAny.cast(true) == {:ok, true}
      assert JsonAny.cast(false) == {:ok, false}
    end

    test "casts lists" do
      assert JsonAny.cast([]) == {:ok, []}
      assert JsonAny.cast([1, 2, 3]) == {:ok, [1, 2, 3]}
      assert JsonAny.cast(["a", "b"]) == {:ok, ["a", "b"]}
      assert JsonAny.cast([%{key: "value"}]) == {:ok, [%{key: "value"}]}
    end

    test "casts maps" do
      assert JsonAny.cast(%{}) == {:ok, %{}}
      assert JsonAny.cast(%{key: "value"}) == {:ok, %{key: "value"}}
      assert JsonAny.cast(%{"nested" => %{"map" => true}}) == {:ok, %{"nested" => %{"map" => true}}}
    end

    test "returns error for unsupported types" do
      assert JsonAny.cast({:tuple, "not supported"}) == :error
      assert JsonAny.cast(self()) == :error
      assert JsonAny.cast(make_ref()) == :error
      assert JsonAny.cast(fn -> :ok end) == :error
    end
  end

  describe "load/1" do
    test "loads any value as-is" do
      assert JsonAny.load(nil) == {:ok, nil}
      assert JsonAny.load("string") == {:ok, "string"}
      assert JsonAny.load(42) == {:ok, 42}
      assert JsonAny.load(3.14) == {:ok, 3.14}
      assert JsonAny.load(true) == {:ok, true}
      assert JsonAny.load(false) == {:ok, false}
      assert JsonAny.load([1, 2, 3]) == {:ok, [1, 2, 3]}
      assert JsonAny.load(%{key: "value"}) == {:ok, %{key: "value"}}
    end

    test "loads complex nested structures" do
      value = %{"users" => [%{"name" => "Alice", "age" => 30}, %{"name" => "Bob", "age" => 25}]}
      assert JsonAny.load(value) == {:ok, value}
    end
  end

  describe "dump/1" do
    test "dumps nil" do
      assert JsonAny.dump(nil) == {:ok, nil}
    end

    test "dumps strings" do
      assert JsonAny.dump("hello") == {:ok, "hello"}
      assert JsonAny.dump("") == {:ok, ""}
    end

    test "dumps integers" do
      assert JsonAny.dump(0) == {:ok, 0}
      assert JsonAny.dump(42) == {:ok, 42}
      assert JsonAny.dump(-100) == {:ok, -100}
    end

    test "dumps floats" do
      assert JsonAny.dump(0.0) == {:ok, 0.0}
      assert JsonAny.dump(3.14) == {:ok, 3.14}
      assert JsonAny.dump(-99.99) == {:ok, -99.99}
    end

    test "dumps booleans" do
      assert JsonAny.dump(true) == {:ok, true}
      assert JsonAny.dump(false) == {:ok, false}
    end

    test "dumps lists" do
      assert JsonAny.dump([]) == {:ok, []}
      assert JsonAny.dump([1, 2, 3]) == {:ok, [1, 2, 3]}
      assert JsonAny.dump(["a", "b"]) == {:ok, ["a", "b"]}
    end

    test "dumps maps" do
      assert JsonAny.dump(%{}) == {:ok, %{}}
      assert JsonAny.dump(%{key: "value"}) == {:ok, %{key: "value"}}

      assert JsonAny.dump(%{"nested" => %{"map" => true}}) ==
               {:ok, %{"nested" => %{"map" => true}}}
    end

    test "dumps complex nested structures" do
      value = %{"users" => [%{"name" => "Alice", "age" => 30}, %{"name" => "Bob", "age" => 25}]}
      assert JsonAny.dump(value) == {:ok, value}
    end

    test "returns error for unsupported types" do
      assert JsonAny.dump({:tuple, "not supported"}) == :error
      assert JsonAny.dump(self()) == :error
      assert JsonAny.dump(make_ref()) == :error
      assert JsonAny.dump(fn -> :ok end) == :error
    end
  end

  describe "cast -> load -> dump roundtrip" do
    test "nil roundtrips correctly" do
      {:ok, casted} = JsonAny.cast(nil)
      {:ok, loaded} = JsonAny.load(casted)
      {:ok, dumped} = JsonAny.dump(loaded)
      assert dumped == nil
    end

    test "string roundtrips correctly" do
      {:ok, casted} = JsonAny.cast("hello")
      {:ok, loaded} = JsonAny.load(casted)
      {:ok, dumped} = JsonAny.dump(loaded)
      assert dumped == "hello"
    end

    test "number roundtrips correctly" do
      {:ok, casted} = JsonAny.cast(42)
      {:ok, loaded} = JsonAny.load(casted)
      {:ok, dumped} = JsonAny.dump(loaded)
      assert dumped == 42
    end

    test "boolean roundtrips correctly" do
      {:ok, casted} = JsonAny.cast(true)
      {:ok, loaded} = JsonAny.load(casted)
      {:ok, dumped} = JsonAny.dump(loaded)
      assert dumped == true
    end

    test "list roundtrips correctly" do
      {:ok, casted} = JsonAny.cast([1, 2, 3])
      {:ok, loaded} = JsonAny.load(casted)
      {:ok, dumped} = JsonAny.dump(loaded)
      assert dumped == [1, 2, 3]
    end

    test "map roundtrips correctly" do
      {:ok, casted} = JsonAny.cast(%{key: "value"})
      {:ok, loaded} = JsonAny.load(casted)
      {:ok, dumped} = JsonAny.dump(loaded)
      assert dumped == %{key: "value"}
    end

    test "complex structure roundtrips correctly" do
      value = %{"users" => [%{"name" => "Alice", "active" => true, "score" => 95.5}]}
      {:ok, casted} = JsonAny.cast(value)
      {:ok, loaded} = JsonAny.load(casted)
      {:ok, dumped} = JsonAny.dump(loaded)
      assert dumped == value
    end
  end
end
