defmodule TarakanWeb.RepositoryPaths do
  @moduledoc """
  Builds repository-scoped paths from a repository's stored host, so no view
  or controller hardcodes a single source host into its links.

  Remote repositories live under their host domain (`/github.com/owner/name`),
  so a source URL pastes directly onto Tarakan. Hosted repositories live at
  GitHub-style bare paths (`/handle/name`); `TarakanWeb.Plugs.HostedRoutes`
  maps that form onto the internal `/hosted` route scope.
  """

  use TarakanWeb, :verified_routes

  alias Tarakan.Repositories.Repository

  def repository_path(repository) do
    repository_root(repository)
  end

  def repository_security_path(repository) do
    repository_root(repository) <> "/security"
  end

  def repository_commits_path(repository) do
    repository_root(repository) <> "/commits"
  end

  def repository_commit_path(repository, commit_sha) do
    repository_commits_path(repository) <> "/" <> encode_path_segment(commit_sha)
  end

  def repository_code_path(repository, commit_sha, path_segments \\ []) do
    root = repository_root(repository) <> "/code/" <> encode_path_segment(commit_sha)

    case path_segments do
      [] -> root
      segments -> root <> "/" <> Enum.map_join(segments, "/", &encode_path_segment/1)
    end
  end

  @doc """
  Clone URLs for a Tarakan-hosted repository, or nil for external hosts.

  The HTTPS URL derives from the endpoint's public URL; the SSH URL is
  included only while the SSH daemon is enabled.
  """
  def clone_urls(repository) do
    if Repository.hosted?(repository) do
      base = TarakanWeb.Endpoint.url()
      path = "#{repository.owner}/#{repository.name}.git"

      %{https: "#{base}/#{path}", ssh: ssh_clone_url(URI.parse(base).host, path)}
    end
  end

  defp ssh_clone_url(host, path) do
    config = Application.get_env(:tarakan, Tarakan.GitSSH, [])

    if Keyword.get(config, :enabled, false) do
      case Keyword.get(config, :port, 22) do
        22 -> "ssh://git@#{host}/#{path}"
        port -> "ssh://git@#{host}:#{port}/#{path}"
      end
    end
  end

  defp repository_root(%{owner: owner, name: name} = repository) do
    if hosted?(repository) do
      "/" <> encode_path_segment(owner) <> "/" <> encode_path_segment(name)
    else
      "/" <>
        encode_path_segment(repository.host) <>
        "/" <> encode_path_segment(owner) <> "/" <> encode_path_segment(name)
    end
  end

  # Projection rows (infestation graph, instance ledger) carry host/owner/name as
  # plain maps rather than loaded structs.
  defp hosted?(%Repository{} = repository), do: Repository.hosted?(repository)
  defp hosted?(%{host: host}), do: host == Repository.hosted_host()

  defp encode_path_segment(segment), do: URI.encode(segment, &URI.char_unreserved?/1)
end
