defmodule Aprsme.ArchiverTest do
  use ExUnit.Case, async: true

  alias Aprsme.Archiver

  describe "start_link/1" do
    test "starts the archiver GenServer with default args" do
      assert {:ok, pid} = Archiver.start_link()
      assert is_pid(pid)
      assert Process.alive?(pid)

      # Clean up
      GenServer.stop(pid)
    end

    test "starts the archiver GenServer with custom args" do
      args = [some: :option]
      assert {:ok, pid} = Archiver.start_link(args)
      assert is_pid(pid)
      assert Process.alive?(pid)

      # Clean up
      GenServer.stop(pid)
    end

    test "registers the process with name :archiver" do
      # Ensure no previous archiver is running
      if Process.whereis(:archiver) do
        GenServer.stop(:archiver)
        Process.sleep(10)
      end

      assert {:ok, pid} = Archiver.start_link()
      assert Process.whereis(:archiver) == pid

      # Clean up
      GenServer.stop(:archiver)
    end

    test "returns error if archiver is already running" do
      # Ensure no previous archiver is running
      if Process.whereis(:archiver) do
        GenServer.stop(:archiver)
        Process.sleep(10)
      end

      assert {:ok, _pid1} = Archiver.start_link()
      assert {:error, {:already_started, _pid}} = Archiver.start_link()

      # Clean up
      GenServer.stop(:archiver)
    end
  end

  describe "init/1" do
    test "initializes with default empty state" do
      assert {:ok, []} = Archiver.init()
    end

    test "initializes with provided state" do
      state = [some: :data]
      assert {:ok, ^state} = Archiver.init(state)
    end

    test "schedules connect message after 5 seconds" do
      # Start the GenServer to test init behavior
      {:ok, pid} = Archiver.start_link()

      # We can't easily test the exact timing without making the test slow,
      # but we can verify the GenServer started successfully
      assert Process.alive?(pid)

      # Clean up
      GenServer.stop(pid)
    end

    test "subscribes to the 'call' topic" do
      # Mock the Endpoint.subscribe call
      # Since we can't easily mock in this context, we'll just verify
      # the GenServer starts without errors
      {:ok, pid} = Archiver.start_link()
      assert Process.alive?(pid)

      # Clean up
      GenServer.stop(pid)
    end
  end

  describe "handle_info/2" do
    setup do
      {:ok, pid} = Archiver.start_link()
      %{pid: pid}
    end

    test "handles :connect message", %{pid: pid} do
      state = [some: :state]

      # Send the connect message directly to test the handler
      result = Archiver.handle_info(:connect, state)

      assert {:noreply, ^state} = result

      # Clean up
      GenServer.stop(pid)
    end

    test "handles unknown messages", %{pid: pid} do
      state = [some: :state]

      # Send an unknown message
      result = Archiver.handle_info(:unknown_message, state)

      assert {:noreply, ^state} = result

      # Clean up
      GenServer.stop(pid)
    end

    test "handles various message types", %{pid: pid} do
      state = [test: :data]

      # Test different message types
      messages = [
        {:some, :tuple},
        "string_message",
        123,
        %{map: "message"},
        [:list, :message]
      ]

      for message <- messages do
        result = Archiver.handle_info(message, state)
        assert {:noreply, ^state} = result
      end

      # Clean up
      GenServer.stop(pid)
    end

    test "preserves state across different messages", %{pid: pid} do
      initial_state = [counter: 0]

      # Test that state is preserved
      result1 = Archiver.handle_info(:connect, initial_state)
      assert {:noreply, ^initial_state} = result1

      result2 = Archiver.handle_info(:other_message, initial_state)
      assert {:noreply, ^initial_state} = result2

      # Clean up
      GenServer.stop(pid)
    end
  end

  describe "GenServer behavior" do
    test "can send messages to running archiver" do
      {:ok, pid} = Archiver.start_link()

      # Send a message and verify the process handles it
      send(pid, :test_message)

      # Give it a moment to process
      Process.sleep(10)

      # Verify the process is still alive (didn't crash)
      assert Process.alive?(pid)

      # Clean up
      GenServer.stop(pid)
    end

    test "can be stopped gracefully" do
      {:ok, pid} = Archiver.start_link()

      assert Process.alive?(pid)

      # Stop the GenServer
      :ok = GenServer.stop(pid)

      # Give it a moment to stop
      Process.sleep(10)

      # Verify it's no longer alive
      refute Process.alive?(pid)
    end

    test "handles multiple concurrent operations" do
      {:ok, pid} = Archiver.start_link()

      # Send multiple messages concurrently
      tasks =
        for i <- 1..10 do
          Task.async(fn ->
            send(pid, {:message, i})
            :ok
          end)
        end

      # Wait for all tasks to complete
      Enum.each(tasks, &Task.await/1)

      # Verify the process is still alive
      assert Process.alive?(pid)

      # Clean up
      GenServer.stop(pid)
    end
  end

  describe "module constants and specs" do
    test "has correct topic constant" do
      # This tests that the module compiles correctly with the @topic attribute
      # The actual value is tested indirectly through the init function
      assert Code.ensure_loaded?(Archiver)
    end

    test "has correct typespec for start_link" do
      # This ensures the module compiles with correct typespecs
      # We can't directly test typespecs, but we can ensure the function
      # behaves according to its spec
      result = Archiver.start_link([])
      assert match?({:ok, _pid}, result) or match?({:error, _reason}, result)

      if match?({:ok, _pid}, result) do
        GenServer.stop(elem(result, 1))
      end
    end

    test "has correct typespec for init" do
      # Test that init returns the expected format
      result = Archiver.init([])
      assert {:ok, []} = result

      result2 = Archiver.init(some: :data)
      assert {:ok, [some: :data]} = result2
    end
  end
end
