defmodule ToweropsWeb.Api.V1.GaiiaWebhookControllerTest do
  use ToweropsWeb.ConnCase, async: true

  import Towerops.AccountsFixtures
  import Towerops.OrganizationsFixtures

  alias Towerops.Gaiia
  alias Towerops.Integrations

  @webhook_secret "test-gaiia-webhook-secret-abc123"

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

    {:ok, integration} =
      Integrations.create_integration(org.id, %{
        provider: "gaiia",
        enabled: true,
        credentials: %{
          "api_key" => "test-api-key",
          "webhook_secret" => @webhook_secret
        }
      })

    %{org: org, integration: integration}
  end

  defp sign_payload(body, secret) do
    :hmac |> :crypto.mac(:sha256, secret, body) |> Base.encode16(case: :lower)
  end

  defp post_webhook(conn, org_id, event, payload, secret \\ @webhook_secret) do
    body = Jason.encode!(%{"event" => event, "data" => payload})
    signature = sign_payload(body, secret)

    conn
    |> put_req_header("content-type", "application/json")
    |> put_req_header("x-gaiia-signature", signature)
    |> post(~p"/api/v1/webhooks/gaiia/#{org_id}", body)
  end

  describe "authentication" do
    test "returns 401 without signature header", %{conn: conn, org: org} do
      conn =
        conn
        |> put_req_header("content-type", "application/json")
        |> post(~p"/api/v1/webhooks/gaiia/#{org.id}", Jason.encode!(%{"event" => "test"}))

      assert json_response(conn, 401)["error"] =~ "Missing"
    end

    test "returns 401 with invalid signature", %{conn: conn, org: org} do
      body = Jason.encode!(%{"event" => "account.created", "data" => %{}})

      conn =
        conn
        |> put_req_header("content-type", "application/json")
        |> put_req_header("x-gaiia-signature", "invalid-signature")
        |> post(~p"/api/v1/webhooks/gaiia/#{org.id}", body)

      assert json_response(conn, 401)["error"] =~ "Invalid"
    end

    test "returns 404 when organization has no gaiia integration", %{conn: conn} do
      other_user = user_fixture()
      other_org = organization_fixture(other_user.id)

      body = Jason.encode!(%{"event" => "account.created", "data" => %{}})
      signature = sign_payload(body, "some-secret")

      conn =
        conn
        |> put_req_header("content-type", "application/json")
        |> put_req_header("x-gaiia-signature", signature)
        |> post(~p"/api/v1/webhooks/gaiia/#{other_org.id}", body)

      assert json_response(conn, 404)["error"] =~ "not found"
    end

    test "returns 401 with wrong secret for organization", %{conn: conn, org: org} do
      conn = post_webhook(conn, org.id, "account.created", %{}, "wrong-secret")
      assert json_response(conn, 401)["error"] =~ "Invalid"
    end
  end

  describe "event processing" do
    test "processes account.created event", %{conn: conn, org: org} do
      payload = %{
        "id" => "gaiia-acct-1",
        "readable_id" => "ACCT-001",
        "name" => "Jane Doe",
        "status" => "active",
        "account_type" => "residential"
      }

      conn = post_webhook(conn, org.id, "account.created", payload)
      assert json_response(conn, 200)["status"] == "ok"

      assert account = Gaiia.get_account(org.id, "gaiia-acct-1")
      assert account.name == "Jane Doe"
    end

    test "processes billing_subscription.activated event", %{conn: conn, org: org} do
      payload = %{
        "id" => "gaiia-sub-1",
        "account_id" => "gaiia-acct-1",
        "status" => "active",
        "product_name" => "50 Mbps Plan",
        "mrr_amount" => "49.99",
        "currency" => "USD"
      }

      conn = post_webhook(conn, org.id, "billing_subscription.activated", payload)
      assert json_response(conn, 200)["status"] == "ok"

      assert sub = Gaiia.get_billing_subscription(org.id, "gaiia-sub-1")
      assert sub.product_name == "50 Mbps Plan"
    end

    test "processes inventory_item.ip_address_assigned event", %{conn: conn, org: org} do
      {:ok, _} =
        Gaiia.upsert_inventory_item(org.id, %{
          gaiia_id: "gaiia-inv-1",
          name: "AP One"
        })

      payload = %{
        "id" => "gaiia-inv-1",
        "name" => "AP One",
        "ip_address" => "10.0.0.50"
      }

      conn = post_webhook(conn, org.id, "inventory_item.ip_address_assigned", payload)
      assert json_response(conn, 200)["status"] == "ok"

      assert item = Gaiia.get_inventory_item(org.id, "gaiia-inv-1")
      assert item.ip_address == "10.0.0.50"
    end

    test "returns ok for unknown events", %{conn: conn, org: org} do
      conn = post_webhook(conn, org.id, "some.future.event", %{})
      assert json_response(conn, 200)["status"] == "ok"
    end
  end

  describe "error handling" do
    test "returns 400 when body is missing event field", %{conn: conn, org: org} do
      body = Jason.encode!(%{"data" => %{}})
      signature = sign_payload(body, @webhook_secret)

      conn =
        conn
        |> put_req_header("content-type", "application/json")
        |> put_req_header("x-gaiia-signature", signature)
        |> post(~p"/api/v1/webhooks/gaiia/#{org.id}", body)

      assert json_response(conn, 400)["error"] =~ "Missing"
    end
  end
end
