defmodule AprsmeWeb.PageController do
  @moduledoc false
  use AprsmeWeb, :controller

  alias Aprsme.Cluster.LeaderElection

  def health(conn, _params) do
    version = :aprsme |> Application.spec(:vsn) |> List.to_string()
    respond_health(conn, version, Application.get_env(:aprsme, :health_status, :healthy))
  end

  defp respond_health(conn, version, :draining) do
    health_error(conn, version, "draining", "Application is draining connections")
  end

  defp respond_health(conn, version, _status) do
    respond_shutdown_or_db(conn, version, Aprsme.ShutdownHandler.shutting_down?())
  end

  defp respond_shutdown_or_db(conn, version, true) do
    health_error(conn, version, "shutting_down", "Application is shutting down")
  end

  defp respond_shutdown_or_db(conn, version, false) do
    respond_db_check(conn, version, db_healthy?())
  end

  defp respond_db_check(conn, version, true) do
    json(conn, %{
      status: "ok",
      version: version,
      database: "connected",
      timestamp: DateTime.utc_now()
    })
  end

  defp respond_db_check(conn, version, false) do
    conn
    |> put_status(503)
    |> json(%{
      status: "error",
      version: version,
      database: "disconnected",
      timestamp: DateTime.utc_now()
    })
  end

  defp health_error(conn, version, status, message) do
    conn
    |> put_status(503)
    |> json(%{
      status: status,
      version: version,
      message: message,
      timestamp: DateTime.utc_now()
    })
  end

  defp db_healthy? do
    _ = Aprsme.Repo.query!("SELECT 1")
    true
  rescue
    _ -> false
  end

  @sitemap_paths ~w(/ /about /api /status /packets)

  def sitemap(conn, _params) do
    base_url = AprsmeWeb.Endpoint.url()

    urls =
      Enum.map(@sitemap_paths, fn path ->
        "  <url><loc>#{base_url}#{path}</loc></url>"
      end)

    xml = """
    <?xml version="1.0" encoding="UTF-8"?>
    <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
    #{Enum.join(urls, "\n")}
    </urlset>
    """

    conn
    |> put_resp_content_type("application/xml")
    |> send_resp(200, xml)
  end

  def api_catalog(conn, _params) do
    base_url = AprsmeWeb.Endpoint.url()
    ws_url = String.replace(base_url, ~r/^http/, "ws")

    body = %{
      linkset: [
        %{
          anchor: "#{base_url}/api/v1",
          "service-doc": [%{href: "#{base_url}/api", type: "text/html"}],
          status: [%{href: "#{base_url}/status.json", type: "application/json"}]
        },
        %{
          anchor: "#{ws_url}/mobile/websocket",
          "service-doc": [
            %{
              href: "https://github.com/gmcintire/aprs.me/blob/main/docs/mobile-api.md",
              type: "text/markdown"
            }
          ],
          status: [%{href: "#{base_url}/status.json", type: "application/json"}]
        }
      ]
    }

    conn
    |> put_resp_content_type("application/linkset+json")
    |> send_resp(200, Jason.encode!(body))
  end

  def ready(conn, _params) do
    # Simple readiness check without database dependency
    # This is faster for startup health checks
    version = :aprsme |> Application.spec(:vsn) |> List.to_string()

    json(conn, %{
      status: "ready",
      version: version,
      timestamp: DateTime.utc_now()
    })
  end

  def status_json(conn, _params) do
    # Get cluster-wide APRS-IS connection status
    aprs_status = LeaderElection.get_cluster_aprs_status()

    # Get application version
    version = :aprsme |> Application.spec(:vsn) |> List.to_string()

    # Calculate uptime in a human-readable format
    uptime_display = format_uptime(aprs_status.uptime_seconds)

    json(conn, %{
      aprs_is: %{
        connected: aprs_status.connected,
        server: aprs_status.server,
        port: aprs_status.port,
        connected_at: aprs_status.connected_at,
        uptime_seconds: aprs_status.uptime_seconds,
        uptime_display: uptime_display,
        login_id: aprs_status.login_id,
        filter: aprs_status.filter
      },
      application: %{
        version: version,
        timestamp: DateTime.utc_now()
      }
    })
  end

  defp format_uptime(seconds) when seconds <= 0, do: "Not connected"

  defp format_uptime(seconds) do
    days = div(seconds, 86_400)
    hours = div(rem(seconds, 86_400), 3600)
    minutes = div(rem(seconds, 3600), 60)
    secs = rem(seconds, 60)
    format_uptime_parts(days, hours, minutes, secs)
  end

  defp format_uptime_parts(d, h, m, s) when d > 0, do: "#{d}d #{h}h #{m}m #{s}s"
  defp format_uptime_parts(_d, h, m, s) when h > 0, do: "#{h}h #{m}m #{s}s"
  defp format_uptime_parts(_d, _h, m, s) when m > 0, do: "#{m}m #{s}s"
  defp format_uptime_parts(_d, _h, _m, s), do: "#{s}s"
end
