defmodule Berrypod.Webhooks do
  @moduledoc """
  Handles incoming webhook events from POD providers.
  """

  alias Berrypod.Orders
  alias Berrypod.Orders.OrderNotifier
  alias Berrypod.Products
  alias Berrypod.Sync.ProductSyncWorker
  alias Berrypod.Webhooks.ProductDeleteWorker

  require Logger

  @doc """
  Handles a Printify webhook event.

  Returns :ok or {:ok, job} on success, {:error, reason} on failure.
  """

  # --- Product events ---

  def handle_printify_event("product:updated", %{"id" => _product_id}) do
    enqueue_product_sync()
  end

  def handle_printify_event("product:publish:started", %{"id" => _product_id}) do
    enqueue_product_sync()
  end

  def handle_printify_event("product:deleted", %{"id" => product_id}) do
    ProductDeleteWorker.enqueue(product_id)
  end

  # --- Order events ---

  def handle_printify_event("order:sent-to-production", resource) do
    with {:ok, order} <- find_order_from_resource(resource) do
      Orders.update_fulfilment(order, %{
        fulfilment_status: "processing",
        provider_status: "in-production"
      })
    end
  end

  def handle_printify_event("order:shipment:created", resource) do
    shipment = extract_shipment(resource)

    with {:ok, order} <- find_order_from_resource(resource),
         {:ok, updated_order} <-
           Orders.update_fulfilment(order, %{
             fulfilment_status: "shipped",
             provider_status: "shipped",
             tracking_number: shipment.tracking_number,
             tracking_url: shipment.tracking_url,
             shipped_at: DateTime.utc_now() |> DateTime.truncate(:second)
           }) do
      OrderNotifier.deliver_shipping_notification(updated_order)
      {:ok, updated_order}
    end
  end

  def handle_printify_event("order:shipment:delivered", resource) do
    with {:ok, order} <- find_order_from_resource(resource) do
      Orders.update_fulfilment(order, %{
        fulfilment_status: "delivered",
        provider_status: "delivered",
        delivered_at: DateTime.utc_now() |> DateTime.truncate(:second)
      })
    end
  end

  # --- Catch-all ---

  def handle_printify_event("shop:disconnected", _resource) do
    Logger.warning("Printify shop disconnected - manual intervention needed")
    :ok
  end

  def handle_printify_event(event_type, _resource) do
    Logger.info("Ignoring unhandled Printify event: #{event_type}")
    :ok
  end

  # =============================================================================
  # Printful events
  # =============================================================================

  @doc """
  Handles a Printful webhook event.

  Returns :ok or {:ok, result} on success, {:error, reason} on failure.
  """

  # --- Order events ---

  def handle_printful_event("package_shipped", data) do
    with {:ok, order} <- find_printful_order(data) do
      shipment = extract_printful_shipment(data)

      {:ok, updated} =
        Orders.update_fulfilment(order, %{
          fulfilment_status: "shipped",
          provider_status: "shipped",
          tracking_number: shipment.tracking_number,
          tracking_url: shipment.tracking_url,
          shipped_at: DateTime.utc_now() |> DateTime.truncate(:second)
        })

      OrderNotifier.deliver_shipping_notification(updated)
      {:ok, updated}
    end
  end

  def handle_printful_event("order_failed", data) do
    with {:ok, order} <- find_printful_order(data) do
      Orders.update_fulfilment(order, %{
        fulfilment_status: "failed",
        provider_status: "failed",
        fulfilment_error: data["reason"] || "Order failed at Printful"
      })
    end
  end

  def handle_printful_event("order_canceled", data) do
    with {:ok, order} <- find_printful_order(data) do
      Orders.update_fulfilment(order, %{
        fulfilment_status: "cancelled",
        provider_status: "canceled"
      })
    end
  end

  # --- Product events ---

  def handle_printful_event("product_updated", _data) do
    enqueue_printful_sync()
  end

  def handle_printful_event("product_synced", _data) do
    enqueue_printful_sync()
  end

  def handle_printful_event("product_deleted", %{"sync_product" => %{"id" => product_id}}) do
    ProductDeleteWorker.enqueue(to_string(product_id))
  end

  def handle_printful_event("product_deleted", _data) do
    enqueue_printful_sync()
  end

  # --- Catch-all ---

  def handle_printful_event(event_type, _data) do
    Logger.info("Ignoring unhandled Printful event: #{event_type}")
    :ok
  end

  # =============================================================================
  # Private helpers
  # =============================================================================

  # --- Printify helpers ---

  defp enqueue_product_sync do
    case Products.get_provider_connection_by_type("printify") do
      nil -> {:error, :no_connection}
      conn -> ProductSyncWorker.enqueue(conn.id)
    end
  end

  defp find_order_from_resource(%{"external_id" => external_id}) when is_binary(external_id) do
    case Orders.get_order_by_number(external_id) do
      nil ->
        Logger.warning("Order webhook: no order found for external_id=#{external_id}")
        {:error, :order_not_found}

      order ->
        {:ok, order}
    end
  end

  defp find_order_from_resource(resource) do
    Logger.warning("Order webhook: missing external_id in resource #{inspect(resource)}")
    {:error, :missing_external_id}
  end

  defp extract_shipment(resource) do
    shipments = resource["shipments"] || []
    shipment = List.last(shipments) || %{}

    %{
      tracking_number: shipment["tracking_number"],
      tracking_url: shipment["tracking_url"]
    }
  end

  # --- Printful helpers ---

  defp enqueue_printful_sync do
    case Products.get_provider_connection_by_type("printful") do
      nil -> {:error, :no_connection}
      conn -> ProductSyncWorker.enqueue(conn.id)
    end
  end

  # Printful order webhooks include external_id in the order data
  defp find_printful_order(%{"order" => %{"external_id" => ext_id}})
       when is_binary(ext_id) and ext_id != "" do
    find_order_by_external_id(ext_id)
  end

  # Fallback: look for external_id at top level
  defp find_printful_order(%{"external_id" => ext_id})
       when is_binary(ext_id) and ext_id != "" do
    find_order_by_external_id(ext_id)
  end

  defp find_printful_order(data) do
    Logger.warning("Printful order webhook: can't find external_id in #{inspect(data)}")
    {:error, :missing_external_id}
  end

  defp find_order_by_external_id(external_id) do
    case Orders.get_order_by_number(external_id) do
      nil ->
        Logger.warning("Order webhook: no order found for external_id=#{external_id}")
        {:error, :order_not_found}

      order ->
        {:ok, order}
    end
  end

  defp extract_printful_shipment(data) do
    shipment = data["shipment"] || %{}

    %{
      tracking_number: shipment["tracking_number"],
      tracking_url: shipment["tracking_url"]
    }
  end
end
