defmodule Francis.Static do
  @moduledoc """
  Utilities for working with static assets.

  This module provides functions to resolve static paths and URLs, including
  support for digested assets generated by `mix francis.digest`.
  """

  @default_manifest_path "priv/static/cache_manifest.json"

  @doc """
  Returns the path for the given asset.

  If a digest manifest is available and the asset is found in it,
  returns the digested path. Otherwise, returns the original path.

  ## Examples

      iex> Francis.Static.static_path("app.css")
      "app-a1b2c3d4.css"
  """
  def static_path(logical_path, base_path \\ "/", manifest_path \\ @default_manifest_path) do
    case load_manifest(manifest_path) do
      {:ok, manifest} ->
        digested_path = get_in(manifest, ["files", logical_path, "digested_path"]) || logical_path

        Path.join(base_path, digested_path)

      _ ->
        Path.join(base_path, logical_path)
    end
  end

  @doc """
  Checks if an asset exists in the manifest.

  ## Examples

      iex> Francis.Static.exists?("app.css")
      true

      iex> Francis.Static.exists?("nonexistent.css")
      false
  """
  def exists?(logical_path, manifest_path \\ @default_manifest_path) do
    case load_manifest(manifest_path) do
      {:ok, manifest} ->
        Map.has_key?(manifest["files"] || %{}, logical_path)

      _ ->
        false
    end
  end

  @doc """
  Returns all assets from the manifest.

  ## Examples

      iex> Francis.Static.all()
      %{
        "app.css" => %{
          "digest" => "a1b2c3d4",
          "digested_path" => "app-a1b2c3d4.css",
          "size" => 1024
        }
      }
  """
  def all(manifest_path \\ @default_manifest_path) do
    case load_manifest(manifest_path) do
      {:ok, manifest} ->
        manifest["files"] || %{}

      _ ->
        %{}
    end
  end

  @doc """
  Loads the cache manifest from the given path.

  Returns `{:ok, manifest}` on success, `{:error, reason}` on failure.

  ## Examples

      iex> Francis.Static.load_manifest("priv/static/cache_manifest.json")
      {:ok, %{"version" => 1, "files" => %{...}}}

      iex> Francis.Static.load_manifest("nonexistent.json")
      {:error, :enoent}
  """
  def load_manifest(manifest_path \\ @default_manifest_path) do
    with {:ok, content} <- File.read(manifest_path) do
      Jason.decode(content)
    end
  end
end
