defmodule Towerops.Preseem.InsightTest do
  use Towerops.DataCase, async: true

  import Towerops.AccountsFixtures
  import Towerops.OrganizationsFixtures

  alias Towerops.Preseem.AccessPoint
  alias Towerops.Preseem.Insight

  setup do
    user = user_fixture()
    org = organization_fixture(user.id)

    {:ok, ap} =
      %AccessPoint{}
      |> AccessPoint.changeset(%{organization_id: org.id, preseem_id: "ap-insight-1", name: "Insight AP"})
      |> Repo.insert()

    %{organization: org, access_point: ap}
  end

  describe "changeset/2" do
    test "valid changeset with all fields", %{organization: org, access_point: ap} do
      attrs = %{
        organization_id: org.id,
        preseem_access_point_id: ap.id,
        type: "qoe_degradation",
        urgency: "warning",
        status: "active",
        channel: "proactive",
        title: "QoE degradation on Tower1-AP",
        description: "QoE score dropped below baseline.",
        metadata: %{"current" => 65.0, "baseline_mean" => 85.0}
      }

      changeset = Insight.changeset(%Insight{}, attrs)
      assert changeset.valid?
    end

    test "requires organization_id" do
      attrs = %{type: "qoe_degradation", urgency: "warning", channel: "proactive", title: "Test"}
      changeset = Insight.changeset(%Insight{}, attrs)
      refute changeset.valid?
      assert "can't be blank" in errors_on(changeset).organization_id
    end

    test "requires type" do
      attrs = %{organization_id: Ecto.UUID.generate(), urgency: "warning", channel: "proactive", title: "Test"}
      changeset = Insight.changeset(%Insight{}, attrs)
      refute changeset.valid?
      assert "can't be blank" in errors_on(changeset).type
    end

    test "requires urgency" do
      attrs = %{organization_id: Ecto.UUID.generate(), type: "qoe_degradation", channel: "proactive", title: "Test"}
      changeset = Insight.changeset(%Insight{}, attrs)
      refute changeset.valid?
      assert "can't be blank" in errors_on(changeset).urgency
    end

    test "requires channel" do
      attrs = %{organization_id: Ecto.UUID.generate(), type: "qoe_degradation", urgency: "warning", title: "Test"}
      changeset = Insight.changeset(%Insight{}, attrs)
      refute changeset.valid?
      assert "can't be blank" in errors_on(changeset).channel
    end

    test "requires title" do
      attrs = %{organization_id: Ecto.UUID.generate(), type: "qoe_degradation", urgency: "warning", channel: "proactive"}
      changeset = Insight.changeset(%Insight{}, attrs)
      refute changeset.valid?
      assert "can't be blank" in errors_on(changeset).title
    end

    test "validates type inclusion" do
      attrs = %{
        organization_id: Ecto.UUID.generate(),
        type: "invalid_type",
        urgency: "warning",
        channel: "proactive",
        title: "Test"
      }

      changeset = Insight.changeset(%Insight{}, attrs)
      refute changeset.valid?
      assert "is invalid" in errors_on(changeset).type
    end

    test "accepts all valid types" do
      valid_types =
        ~w(qoe_degradation capacity_saturation firmware_opportunity model_underperforming subscriber_growth config_drift snmp_cpu_high snmp_memory_high snmp_disk_high device_poll_gap agent_offline firmware_mismatch reconciliation_finding subscriber_growth_trend)

      for type <- valid_types do
        attrs = %{
          organization_id: Ecto.UUID.generate(),
          type: type,
          urgency: "info",
          channel: "passive",
          title: "Test insight"
        }

        changeset = Insight.changeset(%Insight{}, attrs)
        assert changeset.valid?, "Expected type '#{type}' to be valid"
      end
    end

    test "validates urgency inclusion" do
      attrs = %{
        organization_id: Ecto.UUID.generate(),
        type: "qoe_degradation",
        urgency: "extreme",
        channel: "proactive",
        title: "Test"
      }

      changeset = Insight.changeset(%Insight{}, attrs)
      refute changeset.valid?
      assert "is invalid" in errors_on(changeset).urgency
    end

    test "accepts all valid urgencies" do
      for urgency <- ~w(critical warning info) do
        attrs = %{
          organization_id: Ecto.UUID.generate(),
          type: "qoe_degradation",
          urgency: urgency,
          channel: "proactive",
          title: "Test"
        }

        changeset = Insight.changeset(%Insight{}, attrs)
        assert changeset.valid?, "Expected urgency '#{urgency}' to be valid"
      end
    end

    test "validates status inclusion" do
      attrs = %{
        organization_id: Ecto.UUID.generate(),
        type: "qoe_degradation",
        urgency: "warning",
        channel: "proactive",
        title: "Test",
        status: "archived"
      }

      changeset = Insight.changeset(%Insight{}, attrs)
      refute changeset.valid?
      assert "is invalid" in errors_on(changeset).status
    end

    test "accepts all valid statuses" do
      for status <- ~w(active dismissed resolved) do
        attrs = %{
          organization_id: Ecto.UUID.generate(),
          type: "qoe_degradation",
          urgency: "warning",
          channel: "proactive",
          title: "Test",
          status: status
        }

        changeset = Insight.changeset(%Insight{}, attrs)
        assert changeset.valid?, "Expected status '#{status}' to be valid"
      end
    end

    test "validates channel inclusion" do
      attrs = %{
        organization_id: Ecto.UUID.generate(),
        type: "qoe_degradation",
        urgency: "warning",
        channel: "email",
        title: "Test"
      }

      changeset = Insight.changeset(%Insight{}, attrs)
      refute changeset.valid?
      assert "is invalid" in errors_on(changeset).channel
    end

    test "accepts all valid channels" do
      for channel <- ~w(proactive contextual passive) do
        attrs = %{
          organization_id: Ecto.UUID.generate(),
          type: "qoe_degradation",
          urgency: "warning",
          channel: channel,
          title: "Test"
        }

        changeset = Insight.changeset(%Insight{}, attrs)
        assert changeset.valid?, "Expected channel '#{channel}' to be valid"
      end
    end

    test "defaults status to active" do
      attrs = %{
        organization_id: Ecto.UUID.generate(),
        type: "qoe_degradation",
        urgency: "warning",
        channel: "proactive",
        title: "Test"
      }

      changeset = Insight.changeset(%Insight{}, attrs)
      assert Ecto.Changeset.get_field(changeset, :status) == "active"
    end

    test "persists and reloads from database", %{organization: org, access_point: ap} do
      attrs = %{
        organization_id: org.id,
        preseem_access_point_id: ap.id,
        type: "qoe_degradation",
        urgency: "critical",
        channel: "proactive",
        title: "QoE drop on AP",
        description: "Detailed explanation",
        metadata: %{"current" => 60.0, "baseline_mean" => 85.0}
      }

      {:ok, insight} = %Insight{} |> Insight.changeset(attrs) |> Repo.insert()
      reloaded = Repo.get!(Insight, insight.id)

      assert reloaded.organization_id == org.id
      assert reloaded.preseem_access_point_id == ap.id
      assert reloaded.type == "qoe_degradation"
      assert reloaded.urgency == "critical"
      assert reloaded.status == "active"
      assert reloaded.channel == "proactive"
      assert reloaded.title == "QoE drop on AP"
      assert reloaded.description == "Detailed explanation"
      assert reloaded.metadata == %{"current" => 60.0, "baseline_mean" => 85.0}
    end

    test "defaults source to preseem", %{organization: org} do
      attrs = %{
        organization_id: org.id,
        type: "qoe_degradation",
        urgency: "warning",
        channel: "proactive",
        title: "Test"
      }

      changeset = Insight.changeset(%Insight{}, attrs)
      assert Ecto.Changeset.get_field(changeset, :source) == "preseem"
    end

    test "accepts all valid sources", %{organization: org} do
      for source <- ~w(preseem snmp gaiia system) do
        attrs = %{
          organization_id: org.id,
          type: "qoe_degradation",
          urgency: "info",
          channel: "passive",
          title: "Test",
          source: source
        }

        changeset = Insight.changeset(%Insight{}, attrs)
        assert changeset.valid?, "Expected source '#{source}' to be valid"
      end
    end

    test "rejects invalid source", %{organization: org} do
      attrs = %{
        organization_id: org.id,
        type: "qoe_degradation",
        urgency: "warning",
        channel: "proactive",
        title: "Test",
        source: "invalid_source"
      }

      changeset = Insight.changeset(%Insight{}, attrs)
      refute changeset.valid?
      assert "is invalid" in errors_on(changeset).source
    end

    test "accepts new SNMP insight types", %{organization: org} do
      snmp_types = ~w(snmp_cpu_high snmp_memory_high snmp_disk_high device_poll_gap)

      for type <- snmp_types do
        attrs = %{
          organization_id: org.id,
          type: type,
          urgency: "warning",
          channel: "proactive",
          title: "Test",
          source: "snmp"
        }

        changeset = Insight.changeset(%Insight{}, attrs)
        assert changeset.valid?, "Expected type '#{type}' to be valid"
      end
    end

    test "accepts new system insight types", %{organization: org} do
      system_types = ~w(agent_offline firmware_mismatch)

      for type <- system_types do
        attrs = %{
          organization_id: org.id,
          type: type,
          urgency: "warning",
          channel: "proactive",
          title: "Test",
          source: "system"
        }

        changeset = Insight.changeset(%Insight{}, attrs)
        assert changeset.valid?, "Expected type '#{type}' to be valid"
      end
    end

    test "accepts new Gaiia insight types", %{organization: org} do
      gaiia_types = ~w(reconciliation_finding subscriber_growth_trend)

      for type <- gaiia_types do
        attrs = %{
          organization_id: org.id,
          type: type,
          urgency: "info",
          channel: "passive",
          title: "Test",
          source: "gaiia"
        }

        changeset = Insight.changeset(%Insight{}, attrs)
        assert changeset.valid?, "Expected type '#{type}' to be valid"
      end
    end

    test "accepts site_id and agent_token_id fields", %{organization: org} do
      site_id = Ecto.UUID.generate()
      agent_token_id = Ecto.UUID.generate()

      attrs = %{
        organization_id: org.id,
        type: "agent_offline",
        urgency: "critical",
        channel: "proactive",
        title: "Agent offline",
        source: "system",
        site_id: site_id,
        agent_token_id: agent_token_id
      }

      changeset = Insight.changeset(%Insight{}, attrs)
      assert Ecto.Changeset.get_field(changeset, :site_id) == site_id
      assert Ecto.Changeset.get_field(changeset, :agent_token_id) == agent_token_id
    end

    test "persists source and associations", %{organization: org} do
      {:ok, site} =
        Towerops.Sites.create_site(%{name: "Test Site", organization_id: org.id})

      attrs = %{
        organization_id: org.id,
        type: "snmp_cpu_high",
        urgency: "warning",
        channel: "proactive",
        title: "CPU high on site",
        source: "snmp",
        site_id: site.id
      }

      {:ok, insight} = %Insight{} |> Insight.changeset(attrs) |> Repo.insert()
      reloaded = Repo.get!(Insight, insight.id)

      assert reloaded.source == "snmp"
      assert reloaded.site_id == site.id
    end
  end
end
