defmodule Berrypod.MediaTest do
  use Berrypod.DataCase, async: false

  alias Berrypod.Media

  @valid_attrs %{
    image_type: "logo",
    filename: "logo.png",
    content_type: "image/png",
    file_size: 1024,
    data: <<137, 80, 78, 71>>
  }

  @svg_attrs %{
    image_type: "logo",
    filename: "logo.svg",
    content_type: "image/svg+xml",
    file_size: 512,
    data: "<svg xmlns=\"http://www.w3.org/2000/svg\"><circle r=\"10\"/></svg>"
  }

  describe "upload_image/1" do
    test "uploads an image with valid attributes" do
      assert {:ok, image} = Media.upload_image(@valid_attrs)
      assert image.image_type == "logo"
      assert image.filename == "logo.png"
      assert image.content_type == "image/png"
      assert image.is_svg == false
    end

    test "detects and stores SVG content" do
      assert {:ok, image} = Media.upload_image(@svg_attrs)
      assert image.is_svg == true
      assert image.svg_content == @svg_attrs.data
    end

    test "validates required fields" do
      assert {:error, changeset} = Media.upload_image(%{})
      assert "can't be blank" in errors_on(changeset).image_type
      assert "can't be blank" in errors_on(changeset).filename
    end

    test "validates image_type inclusion" do
      attrs = Map.put(@valid_attrs, :image_type, "invalid")
      assert {:error, changeset} = Media.upload_image(attrs)
      assert "is invalid" in errors_on(changeset).image_type
    end

    test "accepts icon image type" do
      attrs = Map.put(@valid_attrs, :image_type, "icon")
      assert {:ok, image} = Media.upload_image(attrs)
      assert image.image_type == "icon"
    end

    test "validates file size" do
      attrs = Map.put(@valid_attrs, :file_size, 15_000_000)
      assert {:error, changeset} = Media.upload_image(attrs)
      assert "must be less than 10000000" in errors_on(changeset).file_size
    end
  end

  describe "get_image/1" do
    test "returns image by id" do
      {:ok, image} = Media.upload_image(@valid_attrs)
      assert ^image = Media.get_image(image.id)
    end

    test "returns nil for nonexistent id" do
      assert Media.get_image(Ecto.UUID.generate()) == nil
    end
  end

  describe "get_logo/0 and get_header/0" do
    test "get_logo returns a logo" do
      {:ok, logo} = Media.upload_image(@valid_attrs)

      result = Media.get_logo()
      assert result.id == logo.id
      assert result.image_type == "logo"
    end

    test "get_header returns most recent header" do
      attrs = Map.put(@valid_attrs, :image_type, "header")
      {:ok, header} = Media.upload_image(attrs)

      result = Media.get_header()
      assert result.id == header.id
    end

    test "get_logo returns nil when no logos exist" do
      assert Media.get_logo() == nil
    end
  end

  describe "delete_image/1" do
    test "deletes an image" do
      {:ok, image} = Media.upload_image(@valid_attrs)
      assert {:ok, _} = Media.delete_image(image)
      assert Media.get_image(image.id) == nil
    end
  end

  describe "list_images_by_type/1" do
    test "lists all images of a specific type" do
      {:ok, logo1} = Media.upload_image(@valid_attrs)
      {:ok, logo2} = Media.upload_image(@valid_attrs)
      {:ok, _header} = Media.upload_image(Map.put(@valid_attrs, :image_type, "header"))

      logos = Media.list_images_by_type("logo")
      assert length(logos) == 2
      assert Enum.any?(logos, &(&1.id == logo1.id))
      assert Enum.any?(logos, &(&1.id == logo2.id))
    end

    test "returns empty list when no images of type exist" do
      assert Media.list_images_by_type("product") == []
    end
  end

  describe "get_icon/0" do
    test "returns the most recent icon image" do
      attrs = Map.put(@valid_attrs, :image_type, "icon")
      {:ok, icon} = Media.upload_image(attrs)

      result = Media.get_icon()
      assert result.id == icon.id
      assert result.image_type == "icon"
    end

    test "returns nil when no icons exist" do
      assert Media.get_icon() == nil
    end
  end

  describe "favicon variants" do
    defp create_source_image do
      {:ok, image} = Media.upload_image(@valid_attrs)
      image
    end

    test "stores and retrieves favicon variants" do
      image = create_source_image()

      {:ok, variants} =
        Media.store_favicon_variants(%{
          source_image_id: image.id,
          png_32: <<1, 2, 3>>,
          png_180: <<4, 5, 6>>,
          png_192: <<7, 8, 9>>,
          png_512: <<10, 11, 12>>
        })

      assert variants.png_32 == <<1, 2, 3>>

      fetched = Media.get_favicon_variants()
      assert fetched.id == variants.id
      assert fetched.png_32 == <<1, 2, 3>>
      assert fetched.png_512 == <<10, 11, 12>>
    end

    test "replaces existing variants on store" do
      image = create_source_image()

      {:ok, first} =
        Media.store_favicon_variants(%{
          source_image_id: image.id,
          png_32: <<1>>,
          png_180: <<2>>,
          png_192: <<3>>,
          png_512: <<4>>
        })

      {:ok, second} =
        Media.store_favicon_variants(%{
          source_image_id: image.id,
          png_32: <<5>>,
          png_180: <<6>>,
          png_192: <<7>>,
          png_512: <<8>>
        })

      assert second.id != first.id
      fetched = Media.get_favicon_variants()
      assert fetched.id == second.id
      assert fetched.png_32 == <<5>>
    end

    test "returns nil when no variants exist" do
      assert Media.get_favicon_variants() == nil
    end
  end

  # ── Media library functions ──────────────────────────────────────

  describe "upload_image/1 with media type" do
    test "accepts media image type" do
      attrs = Map.put(@valid_attrs, :image_type, "media")
      assert {:ok, image} = Media.upload_image(attrs)
      assert image.image_type == "media"
    end

    test "stores alt, caption, and tags on upload" do
      attrs =
        @valid_attrs
        |> Map.put(:alt, "A scenic landscape")
        |> Map.put(:caption, "Taken at sunset")
        |> Map.put(:tags, "nature, landscape")

      assert {:ok, image} = Media.upload_image(attrs)
      assert image.alt == "A scenic landscape"
      assert image.caption == "Taken at sunset"
      assert image.tags == "nature, landscape"
    end
  end

  describe "list_images/1" do
    test "lists all images without BLOBs" do
      {:ok, _} = Media.upload_image(@valid_attrs)
      {:ok, _} = Media.upload_image(Map.put(@valid_attrs, :image_type, "header"))

      images = Media.list_images()
      assert length(images) == 2
      assert Enum.all?(images, &is_nil(&1.data))
    end

    test "filters by type" do
      {:ok, _} = Media.upload_image(@valid_attrs)
      {:ok, _} = Media.upload_image(Map.put(@valid_attrs, :image_type, "header"))

      logos = Media.list_images(type: "logo")
      assert length(logos) == 1
      assert hd(logos).image_type == "logo"
    end

    test "filters by search on filename" do
      {:ok, _} = Media.upload_image(@valid_attrs)
      {:ok, _} = Media.upload_image(Map.put(@valid_attrs, :filename, "banner.png"))

      results = Media.list_images(search: "banner")
      assert length(results) == 1
      assert hd(results).filename == "banner.png"
    end

    test "filters by search on alt text" do
      {:ok, _} = Media.upload_image(Map.merge(@valid_attrs, %{alt: "Mountain sunrise"}))
      {:ok, _} = Media.upload_image(Map.merge(@valid_attrs, %{alt: "Beach sunset"}))

      results = Media.list_images(search: "Mountain")
      assert length(results) == 1
      assert hd(results).alt == "Mountain sunrise"
    end

    test "filters by tag" do
      {:ok, _} = Media.upload_image(Map.merge(@valid_attrs, %{tags: "hero, homepage"}))
      {:ok, _} = Media.upload_image(Map.merge(@valid_attrs, %{tags: "product, gallery"}))

      results = Media.list_images(tag: "hero")
      assert length(results) == 1
      assert hd(results).tags == "hero, homepage"
    end

    test "returns empty list with no matches" do
      {:ok, _} = Media.upload_image(@valid_attrs)
      assert Media.list_images(type: "product") == []
    end
  end

  describe "update_image_metadata/2" do
    test "updates alt, caption, and tags" do
      {:ok, image} = Media.upload_image(@valid_attrs)

      assert {:ok, updated} =
               Media.update_image_metadata(image, %{
                 alt: "New alt text",
                 caption: "A caption",
                 tags: "tag1, tag2"
               })

      assert updated.alt == "New alt text"
      assert updated.caption == "A caption"
      assert updated.tags == "tag1, tag2"
    end

    test "does not affect image data fields" do
      {:ok, image} = Media.upload_image(@valid_attrs)
      original_type = image.image_type

      {:ok, updated} = Media.update_image_metadata(image, %{alt: "Updated"})
      assert updated.image_type == original_type
    end
  end

  describe "find_usages/1" do
    test "returns empty list for unreferenced image" do
      {:ok, image} = Media.upload_image(@valid_attrs)
      assert Media.find_usages(image.id) == []
    end

    test "finds theme setting usages" do
      {:ok, image} = Media.upload_image(@valid_attrs)
      Berrypod.Settings.update_theme_settings(%{logo_image_id: image.id})

      usages = Media.find_usages(image.id)
      assert [%{type: :theme, label: "Logo"}] = usages
    end

    test "finds favicon variant usage" do
      {:ok, image} = Media.upload_image(@valid_attrs)

      Media.store_favicon_variants(%{
        source_image_id: image.id,
        png_32: <<1>>,
        png_180: <<2>>,
        png_192: <<3>>,
        png_512: <<4>>
      })

      usages = Media.find_usages(image.id)
      assert [%{type: :favicon, label: "Favicon source"}] = usages
    end
  end

  describe "used_image_ids/0" do
    test "collects IDs from theme settings" do
      {:ok, image} = Media.upload_image(@valid_attrs)
      Berrypod.Settings.update_theme_settings(%{logo_image_id: image.id})

      ids = Media.used_image_ids()
      assert MapSet.member?(ids, image.id)
    end

    test "returns empty set when nothing is referenced" do
      {:ok, _} = Media.upload_image(@valid_attrs)
      ids = Media.used_image_ids()
      assert MapSet.size(ids) == 0
    end
  end

  describe "delete_with_cleanup/1" do
    test "deletes an unreferenced image" do
      {:ok, image} = Media.upload_image(@valid_attrs)
      assert {:ok, _} = Media.delete_with_cleanup(image)
      assert Media.get_image(image.id) == nil
    end

    test "refuses to delete an image that is in use" do
      {:ok, image} = Media.upload_image(@valid_attrs)
      Berrypod.Settings.update_theme_settings(%{logo_image_id: image.id})

      assert {:error, :in_use, usages} = Media.delete_with_cleanup(image)
      assert length(usages) == 1
      assert Media.get_image(image.id) != nil
    end
  end
end
