defmodule Qtrace.Plotter do
  @moduledoc false

  def plot_histogram(histogram_data, opts \\ []) do
    width = Keyword.get(opts, :width, 60)

    case histogram_data do
      %Qtrace.Histogram{buckets: [_ | _] = buckets, function: {module, function, arity}} ->
        IO.puts(
          "\n#{IO.ANSI.bright()}Latency Histogram: #{inspect(module)}.#{function}/#{arity}#{IO.ANSI.reset()}"
        )

        IO.puts(String.duplicate("=", width + 20))

        plot_horizontal_bars(buckets, width)
        plot_stats(histogram_data)

      _ ->
        IO.puts("No histogram data to plot")
    end

    :ok
  end

  defp plot_horizontal_bars(buckets, width) do
    max_count = buckets |> Enum.map(& &1.count) |> Enum.max()

    # Sort buckets by latency value for better readability
    sorted_buckets = Enum.sort_by(buckets, & &1.midpoint)

    # Print count scale at top
    print_count_scale(max_count, width)

    # Plot each bucket as a horizontal bar
    for bucket <- sorted_buckets do
      formatted_time = format_duration(bucket.midpoint)
      time_label = String.pad_leading(formatted_time, 12)
      bar_length = round(bucket.count / max_count * width)
      bar = String.duplicate("█", bar_length)
      count_label = " (#{bucket.count})"

      IO.puts("#{time_label} │#{bar}#{count_label}")
    end

    # Bottom scale
    IO.puts("#{String.duplicate(" ", 12)} └#{String.duplicate("─", width)}")
    print_count_scale(max_count, width)
  end

  defp print_count_scale(max_count, width) do
    # Print count markers
    markers =
      for i <- [0, 0.25, 0.5, 0.75, 1.0] do
        pos = round(i * width)
        count = round(i * max_count)
        {pos, "#{count}"}
      end

    scale_line = String.duplicate(" ", 13)

    # Build the scale line
    {scale_with_markers, _} =
      Enum.reduce(markers, {scale_line, 0}, fn {pos, label}, {line, last_pos} ->
        # Add spaces to reach position
        spaces_needed = max(0, pos - last_pos)
        new_line = line <> String.duplicate(" ", spaces_needed) <> label
        {new_line, pos + String.length(label)}
      end)

    IO.puts(scale_with_markers)
  end

  defp plot_stats(%Qtrace.Histogram{
         buckets: buckets,
         min: min_val,
         max: max_val,
         percentiles: percentiles
       }) do
    total_samples = Enum.sum(Enum.map(buckets, & &1.count))

    IO.puts("#{IO.ANSI.faint()}Stats:#{IO.ANSI.reset()}")
    IO.puts("  Min: #{format_duration(min_val)}")
    IO.puts("  Max: #{format_duration(max_val)}")
    IO.puts("  Samples: #{total_samples}")

    # Display percentiles
    Enum.each(percentiles, fn {percentile, value} ->
      percentile_label = percentile |> Atom.to_string() |> String.upcase()
      IO.puts("  #{percentile_label}: #{format_duration(value)}")
    end)

    IO.puts("")
  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
