defmodule Berrypod.Sync.ImageDownloadWorker do
  @moduledoc """
  Oban worker for downloading product images from external URLs.

  Downloads images from Printify CDN, processes through the Media pipeline
  (WebP conversion, AVIF/WebP variant generation), and links to ProductImage.

  ## Usage

      # Enqueue a download for a product image
      ImageDownloadWorker.enqueue(product_image_id)

  ## Job Args

    * `product_image_id` - The ID of the ProductImage to download
  """

  use Oban.Worker, queue: :images, max_attempts: 3

  alias Berrypod.Products
  alias Berrypod.Media

  require Logger

  @impl Oban.Worker
  def perform(%Oban.Job{args: %{"product_image_id" => product_image_id}}) do
    case Products.get_product_image(product_image_id) do
      nil ->
        {:cancel, :product_image_not_found}

      %{image_id: image_id} when not is_nil(image_id) ->
        # Already has a linked image, skip
        :ok

      product_image ->
        download_and_link(product_image)
    end
  end

  @doc """
  Enqueue an image download for a product image.
  """
  def enqueue(product_image_id) do
    %{product_image_id: product_image_id}
    |> new()
    |> Oban.insert()
  end

  defp download_and_link(product_image) do
    case download_image(product_image.src) do
      {:ok, data, content_type} ->
        upload_and_link(product_image, data, content_type)

      {:error, reason} ->
        Logger.warning(
          "[ImageDownloadWorker] Failed to download #{product_image.src}: #{inspect(reason)}"
        )

        {:error, reason}
    end
  end

  defp download_image(url) do
    case Req.get(url, receive_timeout: 30_000) do
      {:ok, %Req.Response{status: 200, body: body, headers: headers}} ->
        content_type = get_content_type(headers)
        {:ok, body, content_type}

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

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

  defp get_content_type(headers) do
    headers
    |> Enum.find(fn {k, _v} -> String.downcase(k) == "content-type" end)
    |> case do
      {_, value} when is_binary(value) -> value |> String.split(";") |> hd() |> String.trim()
      {_, [value | _]} -> value |> String.split(";") |> hd() |> String.trim()
      _ -> "image/jpeg"
    end
  end

  defp upload_and_link(product_image, data, content_type) do
    filename = extract_filename(product_image.src, content_type)

    attrs = %{
      image_type: "product",
      filename: filename,
      content_type: content_type,
      file_size: byte_size(data),
      data: data
    }

    case Media.upload_image(attrs) do
      {:ok, image} ->
        case Products.link_product_image(product_image, image.id) do
          {:ok, _} ->
            Logger.info(
              "[ImageDownloadWorker] Downloaded and linked image for #{product_image.id}"
            )

            :ok

          {:error, reason} ->
            Logger.error("[ImageDownloadWorker] Failed to link image: #{inspect(reason)}")
            {:error, reason}
        end

      {:error, reason} ->
        Logger.error("[ImageDownloadWorker] Failed to upload image: #{inspect(reason)}")
        {:error, reason}
    end
  end

  defp extract_filename(url, content_type) do
    # Extract filename from URL path, fall back to generated name
    uri = URI.parse(url)
    path_parts = String.split(uri.path || "", "/")
    basename = List.last(path_parts) || "image"

    # Ensure it has an extension
    if Path.extname(basename) == "" do
      ext = extension_for_content_type(content_type)
      "#{basename}#{ext}"
    else
      basename
    end
  end

  defp extension_for_content_type("image/jpeg"), do: ".jpg"
  defp extension_for_content_type("image/png"), do: ".png"
  defp extension_for_content_type("image/webp"), do: ".webp"
  defp extension_for_content_type("image/gif"), do: ".gif"
  defp extension_for_content_type(_), do: ".jpg"
end
