defmodule Qtrace.Histogram do
  @moduledoc "Histogram generated by using `Qtrace.get_histogram/4`"

  alias DogSketch.SimpleDog

  defstruct [
    :buckets,
    :min,
    :max,
    :function,
    :percentiles
  ]

  @type t :: %__MODULE__{
          buckets: [map()],
          min: number(),
          max: number(),
          function: {module(), atom(), non_neg_integer()},
          percentiles: %{atom() => number()}
        }

  @doc false
  def build_histogram_from_sketch(sketch, module, function, arity) do
    case SimpleDog.to_list(sketch) do
      [] ->
        nil

      bucket_data ->
        buckets =
          bucket_data
          |> Enum.sort_by(fn {value, _count} -> value end)
          |> Enum.map(fn {value, count} ->
            %{
              # Approximate range around the bucket center
              range: {value * 0.95, value * 1.05},
              midpoint: value,
              count: count,
              label: format_duration(value)
            }
          end)

        values = Enum.map(bucket_data, fn {value, _count} -> value end)
        min_val = Enum.min(values)
        max_val = Enum.max(values)

        percentiles = compute_percentiles(sketch)

        %__MODULE__{
          buckets: buckets,
          min: min_val,
          max: max_val,
          function: {module, function, arity},
          percentiles: percentiles
        }
    end
  end

  defp compute_percentiles(sketch) do
    %{
      p50: SimpleDog.quantile(sketch, 0.5),
      p95: SimpleDog.quantile(sketch, 0.95),
      p99: SimpleDog.quantile(sketch, 0.99)
    }
  end

  defp format_duration(microseconds) when microseconds < 1000 do
    "#{round(microseconds)}µs"
  end

  defp format_duration(microseconds) when microseconds < 1_000_000 do
    "#{Float.round(microseconds / 1000, 1)}ms"
  end

  defp format_duration(microseconds) do
    "#{Float.round(microseconds / 1_000_000, 2)}s"
  end
end
