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

  import Towerops.AccountsFixtures
  import Towerops.DevicesFixtures
  import Towerops.OrganizationsFixtures

  alias Towerops.Preseem.AccessPoint
  alias Towerops.Preseem.FleetIntelligence
  alias Towerops.Preseem.FleetProfile

  setup do
    user = user_fixture()
    org = organization_fixture(user.id)
    %{org: org}
  end

  describe "compute_fleet_profiles/1" do
    test "computes profiles grouped by model", %{org: org} do
      for i <- 1..3 do
        insert_matched_ap_with_model(org, "Ubiquiti AF5XHD", %{
          subscriber_count: 30 + i * 5,
          qoe_score: 80.0 + i,
          capacity_score: 70.0 + i
        })
      end

      assert {:ok, 1} = FleetIntelligence.compute_fleet_profiles(org.id)

      profiles = Repo.all(FleetProfile)
      assert length(profiles) == 1
      profile = hd(profiles)
      assert profile.model == "Ubiquiti AF5XHD"
      assert profile.manufacturer == "Ubiquiti"
      assert profile.device_count == 3
      assert profile.avg_subscribers
      assert profile.avg_qoe_score
      assert profile.avg_capacity_score
    end

    test "creates separate profiles for different models", %{org: org} do
      for _i <- 1..2 do
        insert_matched_ap_with_model(org, "Ubiquiti AF5XHD", %{
          subscriber_count: 30,
          qoe_score: 80.0
        })
      end

      for _i <- 1..2 do
        insert_matched_ap_with_model(org, "MikroTik RB4011", %{
          subscriber_count: 20,
          qoe_score: 75.0
        })
      end

      assert {:ok, 2} = FleetIntelligence.compute_fleet_profiles(org.id)

      profiles = Repo.all(FleetProfile)
      assert length(profiles) == 2

      manufacturers = profiles |> Enum.map(& &1.manufacturer) |> Enum.sort()
      assert manufacturers == ["MikroTik", "Ubiquiti"]
    end

    test "handles empty organization", %{org: org} do
      assert {:ok, 0} = FleetIntelligence.compute_fleet_profiles(org.id)
    end

    test "skips APs without model", %{org: org} do
      device = device_fixture(%{organization_id: org.id})

      %AccessPoint{}
      |> AccessPoint.changeset(%{
        organization_id: org.id,
        preseem_id: "ap-no-model-#{System.unique_integer([:positive])}",
        name: "No Model",
        device_id: device.id,
        match_confidence: "auto_ip"
      })
      |> Repo.insert!()

      assert {:ok, 0} = FleetIntelligence.compute_fleet_profiles(org.id)
    end

    test "skips unmatched APs", %{org: org} do
      %AccessPoint{}
      |> AccessPoint.changeset(%{
        organization_id: org.id,
        preseem_id: "ap-unmatched-#{System.unique_integer([:positive])}",
        name: "Unmatched",
        model: "Ubiquiti AF5XHD"
      })
      |> Repo.insert!()

      assert {:ok, 0} = FleetIntelligence.compute_fleet_profiles(org.id)
    end

    test "computes capacity ceiling from APs with good QoE", %{org: org} do
      # AP with high subscribers and good QoE - should define ceiling
      insert_matched_ap_with_model(org, "Ubiquiti AF5XHD", %{
        subscriber_count: 50,
        qoe_score: 85.0
      })

      # AP with even higher subscribers but poor QoE - excluded from ceiling
      insert_matched_ap_with_model(org, "Ubiquiti AF5XHD", %{
        subscriber_count: 80,
        qoe_score: 55.0
      })

      # AP with low subscribers and good QoE
      insert_matched_ap_with_model(org, "Ubiquiti AF5XHD", %{
        subscriber_count: 20,
        qoe_score: 95.0
      })

      assert {:ok, 1} = FleetIntelligence.compute_fleet_profiles(org.id)

      profile = Repo.one!(FleetProfile)
      # Capacity ceiling should be 50 (highest subscriber count with QoE >= 70)
      assert profile.capacity_ceiling == 50
    end

    test "upserts profiles on recomputation", %{org: org} do
      for _i <- 1..3 do
        insert_matched_ap_with_model(org, "Ubiquiti AF5XHD", %{
          subscriber_count: 30,
          qoe_score: 80.0
        })
      end

      assert {:ok, 1} = FleetIntelligence.compute_fleet_profiles(org.id)
      assert length(Repo.all(FleetProfile)) == 1

      # Recompute - should update, not duplicate
      assert {:ok, 1} = FleetIntelligence.compute_fleet_profiles(org.id)
      assert length(Repo.all(FleetProfile)) == 1
    end

    test "includes performance_data with subscriber range and firmware distribution", %{org: org} do
      insert_matched_ap_with_model(org, "Ubiquiti AF5XHD", %{
        subscriber_count: 20,
        firmware: "v4.3.1"
      })

      insert_matched_ap_with_model(org, "Ubiquiti AF5XHD", %{
        subscriber_count: 50,
        firmware: "v4.3.1"
      })

      insert_matched_ap_with_model(org, "Ubiquiti AF5XHD", %{
        subscriber_count: 35,
        firmware: "v4.3.2"
      })

      assert {:ok, 1} = FleetIntelligence.compute_fleet_profiles(org.id)

      profile = Repo.one!(FleetProfile)
      assert profile.performance_data["subscriber_range"]["min"] == 20
      assert profile.performance_data["subscriber_range"]["max"] == 50
      assert profile.performance_data["firmware_distribution"] == %{"v4.3.1" => 2, "v4.3.2" => 1}
    end
  end

  # Helpers

  defp insert_matched_ap_with_model(org, model, extra_attrs) do
    device = device_fixture(%{organization_id: org.id})

    attrs =
      Map.merge(
        %{
          organization_id: org.id,
          preseem_id: "ap-#{System.unique_integer([:positive])}",
          name: "AP",
          model: model,
          device_id: device.id,
          match_confidence: "auto_ip"
        },
        extra_attrs
      )

    %AccessPoint{}
    |> AccessPoint.changeset(attrs)
    |> Repo.insert!()
  end
end
