defmodule Berrypod.Workers.FaviconGeneratorWorker do
  @moduledoc """
  Generates favicon variants from a source image.

  For raster sources: generates PNG variants at 32, 180, 192, and 512px.
  For SVG sources: stores the SVG as-is and attempts PNG generation
  (gracefully skipped if libvips lacks SVG support).
  """
  use Oban.Worker, queue: :images, max_attempts: 3

  require Logger

  alias Berrypod.Media

  @sizes [512, 192, 180, 32]

  @impl Oban.Worker
  def perform(%Oban.Job{args: %{"source_image_id" => id}}) do
    case Media.get_image(id) do
      nil -> {:cancel, :image_not_found}
      image -> generate(image)
    end
  end

  defp generate(%{is_svg: true} = image) do
    # Try to rasterize the SVG for PNG variants (may fail if libvips lacks SVG support)
    png_variants =
      case load_svg(image) do
        {:ok, vips_image} -> generate_png_variants(vips_image)
        {:error, _reason} -> %{}
      end

    attrs = Map.merge(png_variants, %{source_image_id: image.id, svg: image.svg_content})
    Media.store_favicon_variants(attrs)
    :ok
  end

  defp generate(image) do
    case Image.from_binary(image.data) do
      {:ok, vips_image} ->
        png_variants = generate_png_variants(vips_image)
        attrs = Map.put(png_variants, :source_image_id, image.id)
        Media.store_favicon_variants(attrs)
        :ok

      {:error, reason} ->
        Logger.error("Favicon generation failed: #{inspect(reason)}")
        {:error, reason}
    end
  end

  defp load_svg(image) do
    Image.from_svg(image.svg_content)
  rescue
    e ->
      Logger.warning("SVG rasterization unavailable: #{Exception.message(e)}")
      {:error, :svg_unsupported}
  end

  defp generate_png_variants(vips_image) do
    # Crop to square first if needed
    {w, h, _} = Image.shape(vips_image)
    side = min(w, h)

    vips_image =
      if w == h do
        vips_image
      else
        case Image.center_crop(vips_image, side, side) do
          {:ok, cropped} -> cropped
          {:error, _} -> vips_image
        end
      end

    @sizes
    |> Enum.reduce(%{}, fn size, acc ->
      case resize_to_png(vips_image, size) do
        {:ok, png_data} ->
          key = :"png_#{size}"
          Map.put(acc, key, png_data)

        {:error, reason} ->
          Logger.warning("Favicon #{size}px generation failed: #{inspect(reason)}")
          acc
      end
    end)
  end

  defp resize_to_png(vips_image, size) do
    with {:ok, resized} <- Image.thumbnail(vips_image, size),
         {:ok, png_data} <- Image.write(resized, :memory, suffix: ".png") do
      {:ok, png_data}
    end
  end
end
