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

  import Towerops.AccountsFixtures
  import Towerops.IntegrationsFixtures
  import Towerops.OrganizationsFixtures

  alias Towerops.Integrations.Integration
  alias Towerops.Preseem.AccessPoint
  alias Towerops.Preseem.Client
  alias Towerops.Preseem.Sync
  alias Towerops.Preseem.SyncLog

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

    %{org: org, integration: integration}
  end

  describe "upsert_access_point/2" do
    test "creates a new access point from scores API data", %{org: org} do
      ap_data = %{
        "element_uuid" => "preseem-ap-100",
        "name" => "Tower1-AP1",
        "system_mac_address" => "AA:BB:CC:DD:EE:FF",
        "management_ip" => "10.0.1.1",
        "model_name" => "AF60",
        "firmware" => "4.3.2",
        "subscriber_capacity" => 85,
        "ap_health_score" => 92.5,
        "rf_score" => 78.0,
        "airtime_remaining_tx" => 45,
        "connections" => 30
      }

      assert {:ok, ap} = Sync.upsert_access_point(org.id, ap_data)
      assert ap.preseem_id == "preseem-ap-100"
      assert ap.name == "Tower1-AP1"
      assert ap.mac_address == "AA:BB:CC:DD:EE:FF"
      assert ap.ip_address == "10.0.1.1"
      assert ap.model == "AF60"
      assert ap.firmware == "4.3.2"
      assert ap.capacity_score == 85.0
      assert ap.qoe_score == 92.5
      assert ap.rf_score == 78.0
      assert ap.busy_hours == nil
      assert ap.airtime_utilization == 45.0
      assert ap.subscriber_count == 30
      assert ap.organization_id == org.id
      assert ap.match_confidence == "unmatched"
      assert ap.raw_data == ap_data
    end

    test "upserts an existing access point", %{org: org} do
      ap_data = %{
        "element_uuid" => "preseem-ap-200",
        "name" => "Tower1-AP2",
        "system_mac_address" => "11:22:33:44:55:66",
        "management_ip" => "10.0.1.2",
        "model_name" => "AF60",
        "firmware" => "4.3.1",
        "subscriber_capacity" => 70,
        "ap_health_score" => 80.0,
        "rf_score" => 65.0,
        "airtime_remaining_tx" => 30,
        "connections" => 20
      }

      assert {:ok, original} = Sync.upsert_access_point(org.id, ap_data)

      updated_data =
        Map.merge(ap_data, %{
          "name" => "Tower1-AP2-Updated",
          "firmware" => "4.3.2",
          "ap_health_score" => 88.0,
          "connections" => 25
        })

      assert {:ok, updated} = Sync.upsert_access_point(org.id, updated_data)
      assert updated.id == original.id
      assert updated.name == "Tower1-AP2-Updated"
      assert updated.firmware == "4.3.2"
      assert updated.qoe_score == 88.0
      assert updated.subscriber_count == 25
    end

    test "stores full API response as raw_data", %{org: org} do
      ap_data = %{
        "element_uuid" => "preseem-ap-300",
        "name" => "Tower1-AP3",
        "site_name" => "Main Tower",
        "status" => "online"
      }

      assert {:ok, ap} = Sync.upsert_access_point(org.id, ap_data)
      assert ap.raw_data == ap_data
    end
  end

  describe "insert_metrics/2" do
    test "creates a subscriber metric record", %{org: org} do
      {:ok, ap} =
        Sync.upsert_access_point(org.id, %{
          "element_uuid" => "preseem-ap-metrics",
          "name" => "Metrics AP"
        })

      metrics_data = %{
        "avg_latency" => 12.5,
        "avg_jitter" => 3.2,
        "avg_loss" => 0.5,
        "avg_throughput" => 150.0,
        "p95_latency" => 25.0,
        "subscriber_count" => 42,
        "recorded_at" => "2026-02-12T10:00:00Z"
      }

      assert {:ok, metric} = Sync.insert_metrics(ap.id, metrics_data)
      assert metric.preseem_access_point_id == ap.id
      assert metric.avg_latency == 12.5
      assert metric.avg_jitter == 3.2
      assert metric.avg_loss == 0.5
      assert metric.avg_throughput == 150.0
      assert metric.p95_latency == 25.0
      assert metric.subscriber_count == 42
      assert metric.recorded_at == ~U[2026-02-12 10:00:00Z]
    end
  end

  describe "sync_organization/1" do
    test "syncs access points from API and creates sync log", %{
      org: org,
      integration: integration
    } do
      Req.Test.stub(Client, fn conn ->
        Req.Test.json(conn, [
          %{
            "element_uuid" => "ap-sync-1",
            "name" => "Sync AP 1",
            "system_mac_address" => "AA:BB:CC:11:22:33",
            "management_ip" => "10.0.0.1",
            "model_name" => "AF60",
            "firmware" => "4.3.2",
            "subscriber_capacity" => 85,
            "ap_health_score" => 90.0,
            "rf_score" => 75.0,
            "airtime_remaining_tx" => 40,
            "connections" => 25
          },
          %{
            "element_uuid" => "ap-sync-2",
            "name" => "Sync AP 2",
            "system_mac_address" => "AA:BB:CC:44:55:66",
            "management_ip" => "10.0.0.2",
            "model_name" => "LTU-Rocket",
            "firmware" => "2.6.1",
            "subscriber_capacity" => 70,
            "ap_health_score" => 82.0,
            "rf_score" => 68.0,
            "airtime_remaining_tx" => 35,
            "connections" => 18
          }
        ])
      end)

      assert {:ok, result} = Sync.sync_organization(integration)
      assert result.synced == 2

      # Verify APs were created
      aps = Repo.all(from ap in AccessPoint, where: ap.organization_id == ^org.id)
      assert length(aps) == 2
      assert Enum.any?(aps, &(&1.preseem_id == "ap-sync-1"))
      assert Enum.any?(aps, &(&1.preseem_id == "ap-sync-2"))

      # Verify sync log was written
      [log] = Repo.all(from sl in SyncLog, where: sl.integration_id == ^integration.id)
      assert log.status == "success"
      assert log.records_synced == 2
      assert log.organization_id == org.id
      assert log.duration_ms >= 0

      # Verify integration sync status was updated
      updated_integration = Repo.get!(Integration, integration.id)
      assert updated_integration.last_sync_status == "success"
      assert updated_integration.last_synced_at
    end

    test "handles API errors gracefully", %{integration: integration} do
      Req.Test.stub(Client, fn conn ->
        conn
        |> Plug.Conn.put_status(401)
        |> Req.Test.json(%{"error" => "unauthorized"})
      end)

      assert {:error, :unauthorized} = Sync.sync_organization(integration)

      # Verify sync log records the failure
      [log] = Repo.all(from sl in SyncLog, where: sl.integration_id == ^integration.id)
      assert log.status == "failed"
      assert log.records_synced == 0

      # Verify integration sync status was updated to failed
      updated_integration = Repo.get!(Integration, integration.id)
      assert updated_integration.last_sync_status == "failed"
    end
  end
end
