defmodule PlausibleWeb.Api.SystemController do
  use PlausibleWeb, :controller
  require Logger

  def info(conn, _params) do
    build =
      :plausible
      |> Application.get_env(:runtime_metadata)
      |> Keyword.take([:version, :commit, :created, :tags])
      |> Map.new()

    geo_database = Plausible.Geo.database_type() || "(not configured)"

    json(conn, %{
      geo_database: geo_database,
      build: build
    })
  end

  def liveness(conn, _params) do
    json(conn, %{ok: true})
  end

  @task_timeout 15_000
  @critical_caches [
    Plausible.Site.Cache,
    Plausible.Shield.IPRuleCache,
    PlausibleWeb.TrackerScriptCache
  ]
  def readiness(conn, _params) do
    postgres_health_task =
      Task.async(fn ->
        Ecto.Adapters.SQL.query(Plausible.Repo, "SELECT 1", [])
      end)

    clickhouse_health_task =
      Task.async(fn ->
        Ecto.Adapters.SQL.query(Plausible.ClickhouseRepo, "SELECT 1", [])
      end)

    postgres_health =
      case Task.await(postgres_health_task, @task_timeout) do
        {:ok, _} ->
          "ok"

        e ->
          Logger.error("Postgres health check failure: #{inspect(e)}")
          "error"
      end

    clickhouse_health =
      case Task.await(clickhouse_health_task, @task_timeout) do
        {:ok, _} ->
          "ok"

        e ->
          Logger.error("Clickhouse health check failure: #{inspect(e)}")
          "error"
      end

    cache_health =
      if postgres_health == "ok" and Enum.all?(@critical_caches, & &1.ready?()) do
        "ok"
      end

    sessions_health =
      if Plausible.Session.Transfer.attempted?() do
        "ok"
      else
        "waiting"
      end

    status =
      case {postgres_health, clickhouse_health, cache_health, sessions_health} do
        {"ok", "ok", "ok", "ok"} -> 200
        _ -> 500
      end

    put_status(conn, status)
    |> json(%{
      postgres: postgres_health,
      clickhouse: clickhouse_health,
      sites_cache: cache_health,
      sessions: sessions_health
    })
  end
end
