defmodule ArcadiaCloud.Integrations.SkyaiFinance do @moduledoc """ HTTP client for pushing cloud invoices to skyai-finance-svc. Auth: mints a short-lived Guardian JWT (shared secret per project_arcadia_guardian_secret memory) with the platform-admin identity configured under `:arcadia_cloud, :skyai_finance`. JWT carries role `admin` because skyai-finance's RequireWriteRole plug accepts `admin` or `user`, not `platform_admin`. """ alias ArcadiaCloud.Guardian @doc """ Push a single cloud invoice (and ensure-vendor) to skyai-finance. Idempotent — finance dedups on (tenant_id, source, external_id) and updates existing rows on re-push. Returns {:ok, %{vendor_id, invoice_id, action}} | {:error, reason}. """ def push_invoice(payload) when is_map(payload) do with {:ok, base_url} <- fetch(:base_url), {:ok, identity_claims} <- fetch(:identity_claims), {:ok, token, _claims} <- Guardian.encode_and_sign(identity_claims, identity_claims, token_type: :access) do url = base_url <> "/api/v1/integrations/cloud/import-invoice" case Req.post(url, headers: [ {"authorization", "Bearer " <> token}, {"content-type", "application/json"} ], json: payload, retry: :transient, max_retries: 2, receive_timeout: 15_000 ) do {:ok, %Req.Response{status: status, body: body}} when status in 200..299 -> {:ok, body} {:ok, %Req.Response{status: status, body: body}} -> {:error, {:http, status, body}} {:error, e} -> {:error, {:transport, e}} end end end defp fetch(key) do case Application.get_env(:arcadia_cloud, :skyai_finance, [])[key] do nil -> {:error, {:skyai_finance_not_configured, key}} value -> {:ok, value} end end end