defmodule Microwaveprop.LoggerFormatTest do
  # async: false — we briefly swap the global :default :logger handler's
  # formatter and forward its output to a self()-bound IO device so the
  # test can capture the JSON-encoded output. Restored on exit.
  #
  # Opts out of the suite-wide capture_log flag — capture_log hot-swaps
  # the :default handler for its own capture handler, so our direct
  # :logger.get_handler_config(:default) lookup in setup would 404.
  use ExUnit.Case, async: false

  alias LoggerJSON.Formatters.Basic

  require Logger

  @moduletag capture_log: false

  describe "structured JSON formatter" do
    setup do
      {:ok, handler_cfg} = :logger.get_handler_config(:default)
      prev_formatter = handler_cfg.formatter

      new_formatter =
        Basic.new(metadata: [:request_id, :trace_id, :worker, :queue, :job_id])

      :ok = :logger.update_handler_config(:default, :formatter, new_formatter)

      on_exit(fn ->
        :logger.update_handler_config(:default, :formatter, prev_formatter)
      end)

      {:ok, formatter: new_formatter}
    end

    test "Basic formatter encodes an info log as a valid JSON object",
         %{formatter: {formatter_mod, formatter_cfg}} do
      # Build a `:logger` log event the same shape the handler feeds the
      # formatter, then assert the formatter's output is valid JSON with
      # the expected message and metadata fields.
      log_event = %{
        level: :info,
        msg: {:string, "hello-json"},
        meta: %{
          time: :os.system_time(:microsecond),
          request_id: "abc-123",
          worker: "TestWorker"
        }
      }

      rendered = formatter_mod.format(log_event, formatter_cfg)
      payload = rendered |> IO.iodata_to_binary() |> String.trim_trailing("\n")

      assert {:ok, decoded} = Jason.decode(payload)
      assert decoded["message"] == "hello-json"
      assert get_in(decoded, ["metadata", "request_id"]) == "abc-123"
      assert get_in(decoded, ["metadata", "worker"]) == "TestWorker"
    end

    test "swapping the :default handler formatter takes effect" do
      # Smoke test that the setup actually attached our JSON formatter
      # to the :default handler (i.e. the runtime.exs swap would land).
      {:ok, cfg} = :logger.get_handler_config(:default)
      assert match?({Basic, _}, cfg.formatter)
    end
  end
end
