defmodule BerrypodWeb.Plugs.Redirects do
  @moduledoc """
  Plug that handles URL normalisation and custom redirects.

  Three concerns in one pass:
  1. Trailing slash removal (/products/foo/ → /products/foo)
  2. Case normalisation for shop paths (/Products/Foo → /products/foo)
  3. Custom redirect lookup from the redirects table (ETS-cached)

  All redirects preserve query params.

  Note: Order URLs only have their prefix lowercased, not the order number,
  since order numbers contain uppercase hex characters (e.g. SS-260331-5D1A).
  """

  import Plug.Conn
  alias Berrypod.Redirects
  alias BerrypodWeb.R

  # Static prefixes that should always have the full path lowercased (page slugs)
  @static_lowercase_prefixes ~w(/about /delivery /privacy /terms /search /cart /contact /checkout)

  def init(opts), do: opts

  def call(conn, _opts) do
    path = conn.request_path
    stripped = strip_trailing_slash(path)

    cond do
      # Trailing slash — redirect to canonical form
      stripped != path ->
        redirect_to(conn, stripped, 301)

      # Case mismatch on a shop path — redirect to normalised form
      (normalised = normalise_path(path)) != nil and normalised != path ->
        redirect_to(conn, normalised, 301)

      # Check redirect table (ETS-cached)
      :else ->
        case Redirects.lookup(path) do
          {:ok, redirect} ->
            Redirects.increment_hit_count(redirect)
            redirect_to(conn, redirect.to_path, redirect.status_code)

          :not_found ->
            conn
        end
    end
  end

  # Normalise path case. Returns the normalised path or nil if no normalisation needed.
  # For order routes, only the prefix is lowercased (order numbers preserve case).
  # For other routes, the entire path is lowercased.
  defp normalise_path(path) do
    lowered = String.downcase(path)

    # Check static prefixes (full path lowercase)
    if Enum.any?(@static_lowercase_prefixes, &String.starts_with?(lowered, &1)) do
      lowered
    else
      # Check dynamic route prefixes
      segments = path |> String.trim_leading("/") |> String.split("/")
      first_segment = List.first(segments) || ""
      prefix_type = R.prefix_type_from_segment(String.downcase(first_segment))

      case prefix_type do
        nil ->
          # Not a dynamic route
          nil

        :orders ->
          # Order routes: only lowercase the prefix, preserve order number case
          normalise_order_path(segments)

        _ ->
          # Products/collections: lowercase entire path (slugs are always lowercase)
          lowered
      end
    end
  end

  # For order routes, only lowercase the first segment (the prefix)
  defp normalise_order_path([prefix | rest]) do
    lowered_prefix = String.downcase(prefix)

    if lowered_prefix == prefix do
      # Prefix already lowercase, no change needed
      nil
    else
      "/" <> Enum.join([lowered_prefix | rest], "/")
    end
  end

  defp normalise_order_path([]), do: nil

  defp redirect_to(conn, target, status_code) do
    location = append_query(target, conn.query_string)

    conn
    |> put_resp_header("location", location)
    |> send_resp(status_code, "")
    |> halt()
  end

  defp strip_trailing_slash("/"), do: "/"
  defp strip_trailing_slash(path), do: String.trim_trailing(path, "/")

  defp append_query(path, ""), do: path
  defp append_query(path, qs), do: "#{path}?#{qs}"
end
