defmodule ToweropsWeb.Api.V1.GaiiaWebhookController do
  @moduledoc """
  Webhook endpoint for receiving real-time updates from Gaiia.

  Each organization has its own webhook secret stored in the integration
  credentials. The signature is verified using HMAC-SHA256 against the
  raw request body.

  Route: POST /api/v1/webhooks/gaiia/:organization_id
  """
  use ToweropsWeb, :controller

  alias Towerops.Gaiia.Webhooks
  alias Towerops.Integrations

  def create(conn, %{"organization_id" => organization_id}) do
    with {:ok, integration} <- get_gaiia_integration(organization_id),
         {:ok, secret} <- get_webhook_secret(integration),
         {:ok, raw_body} <- get_raw_body(conn),
         :ok <- verify_signature(conn, raw_body, secret),
         {:ok, event} <- get_event(conn.body_params) do
      data = Map.get(conn.body_params, "data", %{})
      payload = %{"entity_id" => Map.get(data, "id"), "data" => data}

      Webhooks.process_event(organization_id, event, payload)

      json(conn, %{status: "ok"})
    end
  end

  defp get_gaiia_integration(organization_id) do
    case Integrations.get_integration(organization_id, "gaiia") do
      {:ok, integration} ->
        {:ok, integration}

      {:error, :not_found} ->
        {:error, :not_found}
    end
  end

  defp get_webhook_secret(integration) do
    case get_in(integration.credentials, ["webhook_secret"]) do
      nil -> {:error, :no_secret}
      secret -> {:ok, secret}
    end
  end

  defp get_raw_body(conn) do
    case conn.private[:raw_body] do
      nil -> {:error, :no_body}
      body -> {:ok, body}
    end
  end

  defp verify_signature(conn, raw_body, secret) do
    case get_req_header(conn, "x-gaiia-signature") do
      [signature] ->
        if Webhooks.verify_signature(raw_body, signature, secret) do
          :ok
        else
          {:error, :invalid_signature}
        end

      _ ->
        {:error, :missing_signature}
    end
  end

  defp get_event(%{"event" => event}) when is_binary(event), do: {:ok, event}
  defp get_event(_), do: {:error, :missing_event}

  # Override action/2 to handle with clause failures
  def action(conn, _opts) do
    case apply(__MODULE__, action_name(conn), [conn, conn.params]) do
      {:error, :not_found} ->
        conn |> put_status(:not_found) |> json(%{error: "Gaiia integration not found"})

      {:error, :no_secret} ->
        conn
        |> put_status(:internal_server_error)
        |> json(%{error: "Webhook secret not configured"})

      {:error, :missing_signature} ->
        conn |> put_status(:unauthorized) |> json(%{error: "Missing signature header"})

      {:error, :invalid_signature} ->
        conn |> put_status(:unauthorized) |> json(%{error: "Invalid webhook signature"})

      {:error, :no_body} ->
        conn |> put_status(:bad_request) |> json(%{error: "Missing request body"})

      {:error, :missing_event} ->
        conn |> put_status(:bad_request) |> json(%{error: "Missing event field"})

      other ->
        other
    end
  end
end
