defmodule Berrypod.Clients.Printify do
  @moduledoc """
  HTTP client for the Printify API.

  Handles authentication and provides low-level API access.
  Requires PRINTIFY_API_TOKEN environment variable to be set.
  """

  @base_url "https://api.printify.com/v1"

  @doc """
  Get the API token.

  Checks process dictionary first (for provider connections with stored credentials),
  then falls back to environment variable (for development/mockup generation).
  """
  def api_token do
    Process.get(:printify_api_key) ||
      System.get_env("PRINTIFY_API_TOKEN") ||
      raise "PRINTIFY_API_TOKEN environment variable is not set"
  end

  @doc """
  Make a GET request to the Printify API.
  """
  def get(path, _opts \\ []) do
    url = @base_url <> path

    case Req.get(url, [headers: auth_headers(), receive_timeout: 30_000] ++ base_options()) do
      {:ok, %Req.Response{status: status, body: body}} when status in 200..299 ->
        {:ok, body}

      {:ok, %Req.Response{status: status, body: body}} ->
        {:error, {status, body}}

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

  @doc """
  Make a POST request to the Printify API.
  """
  def post(path, body, _opts \\ []) do
    url = @base_url <> path

    case Req.post(
           url,
           [json: body, headers: auth_headers(), receive_timeout: 60_000] ++ base_options()
         ) do
      {:ok, %Req.Response{status: status, body: body}} when status in 200..299 ->
        {:ok, body}

      {:ok, %Req.Response{status: status, body: body}} ->
        {:error, {status, body}}

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

  @doc """
  Make a PUT request to the Printify API.
  """
  def put(path, body, _opts \\ []) do
    url = @base_url <> path

    case Req.put(
           url,
           [json: body, headers: auth_headers(), receive_timeout: 60_000] ++ base_options()
         ) do
      {:ok, %Req.Response{status: status, body: body}} when status in 200..299 ->
        {:ok, body}

      {:ok, %Req.Response{status: status, body: body}} ->
        {:error, {status, body}}

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

  @doc """
  Make a DELETE request to the Printify API.
  """
  def delete(path, _opts \\ []) do
    url = @base_url <> path

    case Req.delete(url, [headers: auth_headers(), receive_timeout: 30_000] ++ base_options()) do
      {:ok, %Req.Response{status: 204}} ->
        {:ok, nil}

      {:ok, %Req.Response{status: status, body: body}} when status in 200..299 ->
        {:ok, body}

      {:ok, %Req.Response{status: status, body: body}} ->
        {:error, {status, body}}

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

  @doc """
  Get all shops for the authenticated account.
  """
  def get_shops do
    get("/shops.json")
  end

  @doc """
  Get the first shop ID for the account.
  """
  def get_shop_id do
    case get_shops() do
      {:ok, shops} when is_list(shops) and shops != [] ->
        {:ok, hd(shops)["id"]}

      {:ok, []} ->
        {:error, :no_shops}

      error ->
        error
    end
  end

  @doc """
  Get all blueprints (product types) from the catalog.
  """
  def get_blueprints do
    get("/catalog/blueprints.json")
  end

  @doc """
  Get print providers for a specific blueprint.
  """
  def get_print_providers(blueprint_id) do
    get("/catalog/blueprints/#{blueprint_id}/print_providers.json")
  end

  @doc """
  Get variants for a specific blueprint and print provider.
  """
  def get_variants(blueprint_id, print_provider_id) do
    get("/catalog/blueprints/#{blueprint_id}/print_providers/#{print_provider_id}/variants.json")
  end

  @doc """
  Get shipping information for a blueprint/provider combination.
  """
  def get_shipping(blueprint_id, print_provider_id) do
    get("/catalog/blueprints/#{blueprint_id}/print_providers/#{print_provider_id}/shipping.json")
  end

  @doc """
  Upload an image to Printify via URL.
  """
  def upload_image(file_name, url) do
    post("/uploads/images.json", %{
      file_name: file_name,
      url: url
    })
  end

  @doc """
  Create a product in a shop.
  """
  def create_product(shop_id, product_data) do
    post("/shops/#{shop_id}/products.json", product_data)
  end

  @doc """
  Get a product by ID.
  """
  def get_product(shop_id, product_id) do
    get("/shops/#{shop_id}/products/#{product_id}.json")
  end

  @doc """
  List all products in a shop.
  Printify allows a maximum of 50 products per page.
  """
  def list_products(shop_id, opts \\ []) do
    limit = Keyword.get(opts, :limit, 50)
    page = Keyword.get(opts, :page, 1)
    get("/shops/#{shop_id}/products.json?limit=#{limit}&page=#{page}")
  end

  @doc """
  Update a product in a shop.
  """
  def update_product(shop_id, product_id, product_data) do
    put("/shops/#{shop_id}/products/#{product_id}.json", product_data)
  end

  @doc """
  Delete a product from a shop.
  """
  def delete_product(shop_id, product_id) do
    delete("/shops/#{shop_id}/products/#{product_id}.json")
  end

  @doc """
  Create an order in a shop.
  """
  def create_order(shop_id, order_data) do
    post("/shops/#{shop_id}/orders.json", order_data)
  end

  @doc """
  Get an order by ID.
  """
  def get_order(shop_id, order_id) do
    get("/shops/#{shop_id}/orders/#{order_id}.json")
  end

  # =============================================================================
  # Webhooks
  # =============================================================================

  @doc """
  Register a webhook with Printify.

  ## Event types
  - "product:publish:started"
  - "product:updated"
  - "product:deleted"
  - "shop:disconnected"
  """
  def create_webhook(shop_id, url, topic, secret) do
    post("/shops/#{shop_id}/webhooks.json", %{
      topic: topic,
      url: url,
      secret: secret
    })
  end

  @doc """
  List registered webhooks for a shop.
  """
  def list_webhooks(shop_id) do
    get("/shops/#{shop_id}/webhooks.json")
  end

  @doc """
  Delete a webhook.
  """
  def delete_webhook(shop_id, webhook_id) do
    delete("/shops/#{shop_id}/webhooks/#{webhook_id}.json")
  end

  @doc """
  Download a file from a URL to a local path.
  """
  def download_file(url, output_path) do
    case Req.get(url, into: File.stream!(output_path), receive_timeout: 60_000) do
      {:ok, %Req.Response{status: status}} when status in 200..299 ->
        {:ok, output_path}

      {:ok, %Req.Response{status: status}} ->
        File.rm(output_path)
        {:error, {:http_error, status}}

      {:error, reason} ->
        File.rm(output_path)
        {:error, reason}
    end
  end

  defp auth_headers do
    [
      {"Authorization", "Bearer #{api_token()}"},
      {"Content-Type", "application/json"}
    ]
  end

  defp base_options do
    Application.get_env(:berrypod, __MODULE__, [])[:req_options] || []
  end
end
