defmodule TarakanWeb.InfestationComponents do
  @moduledoc """
  Functional infestation surfaces: ranked pattern table and per-pattern repo matrix.
  """
  use Phoenix.Component

  use Phoenix.VerifiedRoutes,
    endpoint: TarakanWeb.Endpoint,
    router: TarakanWeb.Router,
    statics: TarakanWeb.static_paths()

  alias TarakanWeb.RepositoryPaths

  attr :infestations, :list, required: true
  attr :id, :string, default: "infestation-constellation"
  attr :compact, :boolean, default: false

  @doc "Ranked multi-repo patterns (homepage / index)."
  def constellation(assigns) do
    max_repos =
      assigns.infestations
      |> Enum.map(& &1.repo_count)
      |> Enum.max(fn -> 1 end)

    assigns = assign(assigns, :max_repos, max(max_repos, 1))

    ~H"""
    <div id={@id} class="border-2 border-strong bg-ground">
      <div class="overflow-x-auto">
        <table class="w-full min-w-[36rem] border-collapse text-left">
          <thead>
            <tr class="border-b border-rule font-mono text-[10px] uppercase tracking-[0.12em] text-ink-faint">
              <th class="w-10 px-3 py-2 font-normal sm:px-4">#</th>
              <th class="px-2 py-2 font-normal">Pattern</th>
              <th class="w-[7rem] px-2 py-2 font-normal sm:w-[10rem]">Spread</th>
              <th class="w-16 px-2 py-2 text-right font-normal tabular-nums">Repos</th>
              <th class="hidden w-14 px-2 py-2 text-right font-normal sm:table-cell">Open</th>
              <th class="hidden w-14 px-2 py-2 text-right font-normal md:table-cell">Ver</th>
              <th class="hidden w-14 px-2 py-2 text-right font-normal md:table-cell">Fix</th>
            </tr>
          </thead>
          <tbody class="divide-y divide-rule">
            <tr
              :for={{infestation, i} <- Enum.with_index(@infestations, 1)}
              class="group transition-colors hover:bg-panel"
            >
              <td class="px-3 py-2.5 font-mono text-[11px] tabular-nums text-ink-faint sm:px-4">
                {i}
              </td>
              <td class="min-w-0 px-2 py-2.5">
                <.link
                  id={"#{@id}-hub-#{infestation.pattern_key}"}
                  navigate={~p"/infestations/#{infestation.pattern_key}"}
                  class="block min-w-0"
                >
                  <span class="flex flex-wrap items-center gap-2">
                    <span
                      :if={infestation.severity}
                      class="shrink-0 font-mono text-[10px] uppercase tracking-[0.12em] text-signal"
                    >
                      {infestation.severity}
                    </span>
                    <span class="truncate text-sm font-semibold text-ink group-hover:text-signal">
                      {infestation.title}
                    </span>
                  </span>
                  <span
                    :if={!@compact}
                    class="mt-0.5 block truncate font-mono text-[10px] text-ink-faint"
                  >
                    {String.slice(infestation.pattern_key, 0, 12)}…
                  </span>
                </.link>
              </td>
              <td class="px-2 py-2.5">
                <div class="flex items-center gap-2">
                  <div class="h-1.5 min-w-0 flex-1 bg-rule">
                    <div
                      class="h-full bg-signal"
                      style={"width: #{bar_pct(infestation.repo_count, @max_repos)}%"}
                    >
                    </div>
                  </div>
                </div>
              </td>
              <td class="px-2 py-2.5 text-right font-display text-lg tabular-nums text-ink">
                {infestation.repo_count}
              </td>
              <td class="hidden px-2 py-2.5 text-right font-mono text-xs tabular-nums text-phosphor sm:table-cell">
                {infestation.open_count}
              </td>
              <td class="hidden px-2 py-2.5 text-right font-mono text-xs tabular-nums text-ink md:table-cell">
                {infestation.verified_count}
              </td>
              <td class="hidden px-2 py-2.5 text-right font-mono text-xs tabular-nums text-ink-muted md:table-cell">
                {infestation.fixed_count}
              </td>
            </tr>
          </tbody>
        </table>
      </div>
    </div>
    """
  end

  attr :repos, :list, required: true, doc: "pattern_repos rows: one per repository"
  attr :infestation, :map, required: true
  attr :pattern_key, :string, required: true
  attr :id, :string, default: "infestation-graph"

  @doc """
  Per-repo infestation matrix for one pattern.

  One row per repository, rolled up from that repo's instances. The instance
  grain (one row per finding) is the separate ledger on the same page.
  """
  def infestation_graph(assigns) do
    ~H"""
    <div id={@id} class="border-2 border-strong bg-ground">
      <div
        :if={@repos == []}
        class="px-4 py-10 text-center font-mono text-xs text-ink-faint"
      >
        No listed public repositories.
      </div>

      <div :if={@repos != []} class="overflow-x-auto">
        <table class="w-full min-w-[32rem] border-collapse text-left">
          <thead>
            <tr class="border-b border-rule font-mono text-[10px] uppercase tracking-[0.12em] text-ink-faint">
              <th class="w-8 px-3 py-2 font-normal sm:px-4"></th>
              <th class="px-2 py-2 font-normal">Repository</th>
              <th class="w-24 px-2 py-2 font-normal">Status</th>
              <th class="w-20 px-2 py-2 text-right font-normal tabular-nums">Findings</th>
              <th class="hidden w-16 px-2 py-2 text-right font-normal tabular-nums sm:table-cell">
                Open
              </th>
              <th class="w-20 px-3 py-2 text-right font-normal sm:px-4"></th>
            </tr>
          </thead>
          <tbody class="divide-y divide-rule">
            <tr
              :for={repo <- @repos}
              id={"#{@id}-node-#{repo.id}"}
              class="group transition-colors hover:bg-panel"
            >
              <td class="px-3 py-2.5 sm:px-4">
                <span class={[
                  "inline-block size-2 rounded-full",
                  repo.status == "open" && "bg-phosphor",
                  repo.status == "verified" && "bg-ink",
                  repo.status == "fixed" && "bg-ink-muted",
                  repo.status not in ["open", "verified", "fixed"] && "bg-ink-faint"
                ]}></span>
              </td>
              <td class="min-w-0 px-2 py-2.5">
                <.link
                  :if={repo[:host]}
                  navigate={RepositoryPaths.repository_path(repo)}
                  class="block truncate font-mono text-xs font-semibold text-ink hover:text-signal hover:underline"
                >
                  {repo.owner}/{repo.name}
                </.link>
                <span
                  :if={is_nil(repo[:host])}
                  class="block truncate font-mono text-xs font-semibold text-ink"
                >
                  {repo.owner}/{repo.name}
                </span>
              </td>
              <td class="px-2 py-2.5">
                <span class={[
                  "font-mono text-[10px] uppercase tracking-[0.12em]",
                  repo.status == "open" && "text-phosphor",
                  repo.status == "verified" && "text-ink",
                  repo.status == "fixed" && "text-ink-muted",
                  repo.status not in ["open", "verified", "fixed"] && "text-ink-faint"
                ]}>
                  {repo.status}
                </span>
              </td>
              <td class="px-2 py-2.5 text-right font-mono text-xs tabular-nums text-ink">
                {repo[:instance_count] || 1}
              </td>
              <td class={[
                "hidden px-2 py-2.5 text-right font-mono text-xs tabular-nums sm:table-cell",
                (repo[:open_count] || 0) > 0 && "text-phosphor",
                (repo[:open_count] || 0) == 0 && "text-ink-faint"
              ]}>
                {repo[:open_count] || 0}
              </td>
              <td class="px-3 py-2.5 text-right sm:px-4">
                <.link
                  :if={repo.occurrence_public_id}
                  navigate={~p"/findings/#{repo.occurrence_public_id}"}
                  class="font-mono text-[11px] text-signal hover:underline"
                >
                  Finding →
                </.link>
              </td>
            </tr>
          </tbody>
        </table>
      </div>
    </div>
    """
  end

  defp bar_pct(count, max) when max > 0 do
    count
    |> Kernel./(max)
    |> Kernel.*(100)
    |> Float.round(1)
    |> max(4)
    |> min(100)
  end

  defp bar_pct(_, _), do: 0
end
