defmodule Microwaveprop.Radio.EnrichmentStatusTest do
  use Microwaveprop.DataCase, async: true

  alias Microwaveprop.Radio.Contact
  alias Microwaveprop.Radio.EnrichmentStatus

  describe "states/0" do
    test "returns all valid states" do
      assert EnrichmentStatus.states() == [:pending, :queued, :processing, :complete, :failed, :unavailable]
    end
  end

  describe "fields/0" do
    test "returns all status fields" do
      assert EnrichmentStatus.fields() == [:hrrr_status, :weather_status, :terrain_status, :iemre_status]
    end
  end

  describe "valid_transition?/2" do
    test "pending can transition to queued" do
      assert EnrichmentStatus.valid_transition?(:pending, :queued)
    end

    test "pending can transition to unavailable" do
      assert EnrichmentStatus.valid_transition?(:pending, :unavailable)
    end

    test "pending cannot transition to complete" do
      refute EnrichmentStatus.valid_transition?(:pending, :complete)
    end

    test "queued can transition to processing" do
      assert EnrichmentStatus.valid_transition?(:queued, :processing)
    end

    test "queued can transition to failed" do
      assert EnrichmentStatus.valid_transition?(:queued, :failed)
    end

    test "queued cannot transition to complete" do
      refute EnrichmentStatus.valid_transition?(:queued, :complete)
    end

    test "processing can transition to complete" do
      assert EnrichmentStatus.valid_transition?(:processing, :complete)
    end

    test "processing can transition to failed" do
      assert EnrichmentStatus.valid_transition?(:processing, :failed)
    end

    test "processing cannot transition to pending" do
      refute EnrichmentStatus.valid_transition?(:processing, :pending)
    end

    test "failed can transition to queued (retry)" do
      assert EnrichmentStatus.valid_transition?(:failed, :queued)
    end

    test "failed cannot transition to complete" do
      refute EnrichmentStatus.valid_transition?(:failed, :complete)
    end

    test "unavailable can transition to queued (retry)" do
      assert EnrichmentStatus.valid_transition?(:unavailable, :queued)
    end

    test "complete can transition to pending (reset)" do
      assert EnrichmentStatus.valid_transition?(:complete, :pending)
    end

    test "complete cannot transition to queued" do
      refute EnrichmentStatus.valid_transition?(:complete, :queued)
    end
  end

  describe "transition/3" do
    test "applies valid transition to changeset" do
      changeset =
        %Contact{}
        |> Ecto.Changeset.change(%{hrrr_status: :pending})
        |> EnrichmentStatus.transition(:hrrr_status, :queued)

      assert Ecto.Changeset.get_change(changeset, :hrrr_status) == :queued
      assert changeset.valid?
    end

    test "adds error for invalid transition" do
      changeset =
        %Contact{}
        |> Ecto.Changeset.change(%{hrrr_status: :pending})
        |> EnrichmentStatus.transition(:hrrr_status, :complete)

      assert {:hrrr_status, {"invalid transition from pending to complete", []}} in changeset.errors
    end

    test "works for all status fields" do
      for field <- EnrichmentStatus.fields() do
        changeset =
          %Contact{}
          |> Ecto.Changeset.change(%{field => :pending})
          |> EnrichmentStatus.transition(field, :queued)

        assert Ecto.Changeset.get_change(changeset, field) == :queued
      end
    end
  end

  describe "force_status/3" do
    test "sets status without transition validation" do
      changeset =
        %Contact{}
        |> Ecto.Changeset.change(%{hrrr_status: :pending})
        |> EnrichmentStatus.force_status(:hrrr_status, :complete)

      assert Ecto.Changeset.get_change(changeset, :hrrr_status) == :complete
      assert changeset.valid?
    end

    test "allows normally invalid transitions" do
      changeset =
        %Contact{}
        |> Ecto.Changeset.change(%{weather_status: :pending})
        |> EnrichmentStatus.force_status(:weather_status, :failed)

      assert Ecto.Changeset.get_change(changeset, :weather_status) == :failed
      assert changeset.valid?
    end
  end
end
