defmodule Plausible.InstallationSupport.Verification.Checks do
  @moduledoc """
  Checks that are performed during tracker script installation verification.

  In async execution, each check notifies the caller by sending a message to it.
  """
  alias Plausible.InstallationSupport.Verification
  alias Plausible.InstallationSupport.{State, CheckRunner, Checks}

  require Logger

  @checks [
    Checks.Url,
    Checks.InstallationV2,
    Checks.InstallationV2CacheBust
  ]

  @spec run(String.t(), String.t(), String.t(), Keyword.t()) :: :ok
  def run(url, data_domain, installation_type, opts \\ []) do
    report_to = Keyword.get(opts, :report_to, self())
    async? = Keyword.get(opts, :async?, true)
    slowdown = Keyword.get(opts, :slowdown, 500)

    init_state =
      %State{
        url: url,
        data_domain: data_domain,
        report_to: report_to,
        diagnostics: %Verification.Diagnostics{
          selected_installation_type: installation_type
        }
      }

    CheckRunner.run(init_state, @checks,
      async?: async?,
      report_to: report_to,
      slowdown: slowdown
    )
  end

  def telemetry_event_handled(), do: [:plausible, :verification, :handled]
  def telemetry_event_unhandled(), do: [:plausible, :verification, :unhandled]

  def interpret_diagnostics(
        %State{
          diagnostics: diagnostics,
          data_domain: data_domain,
          url: url
        },
        opts \\ []
      ) do
    telemetry? = Keyword.get(opts, :telemetry?, true)

    result =
      Verification.Diagnostics.interpret(
        diagnostics,
        data_domain,
        url
      )

    case {telemetry?, result.data} do
      {false, _} ->
        :skip

      {_, %{unhandled: true}} ->
        Sentry.capture_message("Unhandled case for site verification (v2)",
          extra: %{
            message: inspect(diagnostics),
            url: url,
            hash: :erlang.phash2(diagnostics)
          }
        )

        Logger.warning(
          "[VERIFICATION v2] Unhandled case (data_domain='#{data_domain}'): #{inspect(diagnostics)}"
        )

        :telemetry.execute(telemetry_event_unhandled(), %{})

      _ ->
        :telemetry.execute(telemetry_event_handled(), %{})
    end

    result
  end
end
