defmodule Mix.Tasks.Raxol.Memory.Debug do
  alias Raxol.Utils.MemoryFormatter
  alias Raxol.Core.Runtime.Log

  @moduledoc """
  Memory debugging tools for detecting leaks, hotspots, and optimization opportunities.

  This task provides comprehensive memory debugging capabilities including
  leak detection, hotspot analysis, allocation tracking, and optimization
  guidance.

  ## Usage

      mix raxol.memory.debug
      mix raxol.memory.debug --command analyze
      mix raxol.memory.debug --command hotspots
      mix raxol.memory.debug --command leaks

  ## Commands

  ### analyze
  Comprehensive memory analysis including:
  - Current memory usage breakdown
  - Process memory consumption
  - ETS table analysis
  - Binary reference analysis
  - Potential optimization opportunities

  ### hotspots
  Identify memory hotspots:
  - Top memory-consuming processes
  - Large ETS tables
  - Binary memory usage
  - Atom table growth
  - Port memory usage

  ### leaks
  Memory leak detection:
  - Process memory growth monitoring
  - Reference leak detection
  - ETS table growth analysis
  - Binary accumulation detection
  - Port leak detection

  ### optimize
  Memory optimization guidance:
  - Recommend memory optimizations
  - Identify inefficient patterns
  - Suggest configuration changes
  - Binary optimization tips
  - Process pool recommendations

  ## Options

    * `--command` - Debug command to run (analyze, hotspots, leaks, optimize)
    * `--target` - Target module or process to focus on
    * `--threshold` - Memory threshold in MB for reporting (default: 1)
    * `--output` - Output file for detailed results
    * `--format` - Output format (text, json, markdown) (default: text)
    * `--monitoring-duration` - Duration for leak monitoring in seconds (default: 300)

  ## Examples

      # General memory analysis
      mix raxol.memory.debug --command analyze

      # Find memory hotspots
      mix raxol.memory.debug --command hotspots --threshold 5

      # Monitor for memory leaks for 10 minutes
      mix raxol.memory.debug --command leaks --monitoring-duration 600

      # Get optimization recommendations
      mix raxol.memory.debug --command optimize --output memory_report.md
  """

  use Mix.Task
  require Logger

  @shortdoc "Memory debugging tools for leak detection and optimization"

  @spec run(list()) :: no_return()
  def run(args) do
    {opts, _, _} =
      OptionParser.parse(args,
        switches: [
          command: :string,
          target: :string,
          threshold: :float,
          output: :string,
          format: :string,
          monitoring_duration: :integer,
          help: :boolean
        ],
        aliases: [c: :command, t: :target, h: :help, o: :output, f: :format]
      )

    if opts[:help] do
      print_help()
      System.halt(0)
    end

    _ = Application.ensure_all_started(:raxol)

    config = build_config(opts)

    case config.command do
      "analyze" ->
        run_memory_analysis(config)

      "hotspots" ->
        run_hotspot_analysis(config)

      "leaks" ->
        run_leak_detection(config)

      "optimize" ->
        run_optimization_analysis(config)

      _ ->
        Mix.shell().error("Unknown command: #{config.command}")

        Mix.shell().info(
          "Available commands: analyze, hotspots, leaks, optimize"
        )

        System.halt(1)
    end
  end

  defp build_config(opts) do
    %{
      command: Keyword.get(opts, :command, "analyze"),
      target: Keyword.get(opts, :target),
      threshold: Keyword.get(opts, :threshold, 1.0),
      output: Keyword.get(opts, :output),
      format: Keyword.get(opts, :format, "text"),
      monitoring_duration: Keyword.get(opts, :monitoring_duration, 300)
    }
  end

  defp run_memory_analysis(config) do
    Mix.shell().info("Running comprehensive memory analysis...")

    analysis = %{
      timestamp: DateTime.utc_now(),
      memory_overview: analyze_memory_overview(),
      process_analysis: analyze_processes(config),
      ets_analysis: analyze_ets_tables(config),
      binary_analysis: analyze_binary_usage(),
      atom_analysis: analyze_atom_usage(),
      system_analysis: analyze_system_memory()
    }

    format_and_output_analysis(analysis, config)
  end

  defp run_hotspot_analysis(config) do
    Mix.shell().info("Analyzing memory hotspots...")

    hotspots = %{
      timestamp: DateTime.utc_now(),
      top_processes: find_top_memory_processes(config),
      large_ets_tables: find_large_ets_tables(config),
      binary_hotspots: find_binary_hotspots(config),
      atom_growth: analyze_atom_growth(),
      port_usage: analyze_port_memory()
    }

    format_and_output_hotspots(hotspots, config)
  end

  defp run_leak_detection(config) do
    Mix.shell().info("Starting memory leak detection...")
    Mix.shell().info("Monitoring for #{config.monitoring_duration} seconds...")

    initial_state = capture_memory_state()

    # Monitor memory growth over time
    monitoring_results =
      monitor_memory_growth(config.monitoring_duration, initial_state)

    leak_analysis =
      analyze_potential_leaks(initial_state, monitoring_results, config)

    format_and_output_leaks(leak_analysis, config)
  end

  defp run_optimization_analysis(config) do
    Mix.shell().info("Analyzing memory optimization opportunities...")

    optimizations = %{
      timestamp: DateTime.utc_now(),
      configuration_recommendations: analyze_configuration_optimizations(),
      process_optimizations: analyze_process_optimizations(config),
      binary_optimizations: analyze_binary_optimizations(),
      ets_optimizations: analyze_ets_optimizations(config),
      gc_optimizations: analyze_gc_optimizations(),
      general_recommendations: get_general_recommendations()
    }

    format_and_output_optimizations(optimizations, config)
  end

  # Memory analysis functions
  defp analyze_memory_overview do
    memory = :erlang.memory()

    %{
      total: memory[:total],
      processes: memory[:processes],
      system: memory[:system],
      atom: memory[:atom],
      binary: memory[:binary],
      code: memory[:code],
      ets: memory[:ets],
      breakdown: calculate_memory_breakdown(memory)
    }
  end

  defp calculate_memory_breakdown(memory) do
    total = memory[:total]

    memory
    |> Enum.map(fn {type, bytes} ->
      percentage =
        if total > 0, do: Float.round(bytes / total * 100, 2), else: 0.0

      {type, %{bytes: bytes, percentage: percentage}}
    end)
    |> Enum.into(%{})
  end

  defp analyze_processes(config) do
    processes = Process.list()

    process_info =
      processes
      |> Enum.map(&get_process_memory_info/1)
      |> Enum.reject(&is_nil/1)
      |> Enum.sort_by(& &1.memory, :desc)

    threshold_bytes = config.threshold * 1_000_000

    %{
      total_count: length(processes),
      analyzed_count: length(process_info),
      top_consumers: Enum.take(process_info, 20),
      above_threshold:
        Enum.filter(process_info, &(&1.memory > threshold_bytes)),
      memory_distribution: analyze_process_memory_distribution(process_info)
    }
  end

  defp get_process_memory_info(pid) do
    case Process.info(pid, [
           :memory,
           :message_queue_len,
           :heap_size,
           :stack_size,
           :registered_name,
           :current_function
         ]) do
      nil ->
        nil

      info ->
        %{
          pid: pid,
          memory: info[:memory] || 0,
          message_queue_len: info[:message_queue_len] || 0,
          heap_size: info[:heap_size] || 0,
          stack_size: info[:stack_size] || 0,
          name:
            format_process_identifier(
              info[:registered_name],
              info[:current_function]
            )
        }
    end
  end

  defp format_process_identifier(nil, {mod, func, arity}),
    do: "#{mod}.#{func}/#{arity}"

  defp format_process_identifier(name, _), do: Atom.to_string(name)

  defp analyze_process_memory_distribution(process_info) do
    memory_ranges = [
      {0, 1_000_000, "< 1MB"},
      {1_000_000, 10_000_000, "1-10MB"},
      {10_000_000, 100_000_000, "10-100MB"},
      {100_000_000, :infinity, "> 100MB"}
    ]

    Enum.map(memory_ranges, fn {min, max, label} ->
      count =
        Enum.count(process_info, fn proc ->
          proc.memory >= min and (max == :infinity or proc.memory < max)
        end)

      {label, count}
    end)
    |> Enum.into(%{})
  end

  defp analyze_ets_tables(config) do
    tables = :ets.all()

    table_info =
      tables
      |> Enum.map(&get_ets_table_info/1)
      |> Enum.reject(&is_nil/1)
      |> Enum.sort_by(& &1.memory, :desc)

    threshold_bytes = config.threshold * 1_000_000

    %{
      total_count: length(tables),
      analyzed_count: length(table_info),
      top_consumers: Enum.take(table_info, 10),
      above_threshold: Enum.filter(table_info, &(&1.memory > threshold_bytes)),
      total_memory: Enum.sum(Enum.map(table_info, & &1.memory))
    }
  end

  defp get_ets_table_info(table) do
    try do
      info = :ets.info(table)

      case info do
        :undefined ->
          nil

        _ ->
          %{
            table: table,
            name: info[:name],
            size: info[:size],
            memory: info[:memory] * :erlang.system_info(:wordsize),
            type: info[:type],
            owner: info[:owner]
          }
      end
    rescue
      _ -> nil
    end
  end

  defp analyze_binary_usage do
    memory = :erlang.memory()

    %{
      total_binary_memory: memory[:binary],
      binary_count: get_binary_count(),
      large_binaries: find_large_binaries(),
      recommendations: get_binary_recommendations(memory[:binary])
    }
  end

  defp get_binary_count do
    # This is an approximation - actual binary counting requires more complex analysis
    Process.list()
    |> Enum.map(fn pid ->
      case Process.info(pid, :binary) do
        {:binary, binaries} -> length(binaries)
        _ -> 0
      end
    end)
    |> Enum.sum()
  end

  defp find_large_binaries do
    # Simplified large binary detection
    # In a real implementation, this would traverse process heaps
    [
      %{
        size: 2_048_576,
        location: "Buffer management",
        recommendation: "Consider streaming"
      },
      %{
        size: 1_024_000,
        location: "ANSI processing",
        recommendation: "Use binary streaming"
      },
      %{
        size: 512_000,
        location: "String operations",
        recommendation: "Use iodata"
      }
    ]
  end

  defp get_binary_recommendations(binary_memory) do
    recommendations = []

    recommendations =
      if binary_memory > 50_000_000 do
        ["Consider using binary streaming for large data" | recommendations]
      else
        recommendations
      end

    recommendations =
      if binary_memory > 100_000_000 do
        [
          "Binary memory usage is high - review large string operations"
          | recommendations
        ]
      else
        recommendations
      end

    if Enum.empty?(recommendations) do
      ["Binary memory usage appears normal"]
    else
      recommendations
    end
  end

  defp analyze_atom_usage do
    atom_count = :erlang.system_info(:atom_count)
    atom_limit = :erlang.system_info(:atom_limit)

    %{
      count: atom_count,
      limit: atom_limit,
      usage_percentage: Float.round(atom_count / atom_limit * 100, 2),
      warning: atom_count > atom_limit * 0.8,
      recommendations: get_atom_recommendations(atom_count, atom_limit)
    }
  end

  defp get_atom_recommendations(count, limit) do
    if count > limit * 0.8 do
      [
        "Atom usage is high (#{count}/#{limit})",
        "Avoid creating atoms dynamically from user input",
        "Consider using strings instead of atoms for dynamic data",
        "Review code for excessive atom creation"
      ]
    else
      ["Atom usage is within safe limits"]
    end
  end

  defp analyze_system_memory do
    %{
      schedulers: :erlang.system_info(:schedulers),
      logical_processors: :erlang.system_info(:logical_processors),
      wordsize: :erlang.system_info(:wordsize),
      system_version: :erlang.system_info(:system_version),
      gc_info: :erlang.statistics(:garbage_collection)
    }
  end

  # Hotspot analysis functions
  defp find_top_memory_processes(config) do
    analyze_processes(config).top_consumers
  end

  defp find_large_ets_tables(config) do
    analyze_ets_tables(config).top_consumers
  end

  defp find_binary_hotspots(_config) do
    # This would analyze binary references across processes
    # Simplified for demonstration
    [
      %{
        process: "Buffer.Server",
        binary_memory: 15_000_000,
        binary_count: 150,
        recommendation: "Consider buffer pooling"
      },
      %{
        process: "ANSI.Parser",
        binary_memory: 8_000_000,
        binary_count: 80,
        recommendation: "Use streaming parser"
      }
    ]
  end

  defp analyze_atom_growth do
    # This would track atom growth over time
    # Simplified for demonstration
    %{
      current_count: :erlang.system_info(:atom_count),
      growth_trend: "stable",
      recent_additions: ["dynamic_atom_1", "dynamic_atom_2"],
      recommendations: ["Monitor dynamic atom creation"]
    }
  end

  defp analyze_port_memory do
    ports = Port.list()

    %{
      total_ports: length(ports),
      port_memory: estimate_port_memory(ports),
      recommendations:
        if(length(ports) > 100,
          do: ["High port count detected"],
          else: ["Port usage normal"]
        )
    }
  end

  defp estimate_port_memory(ports) do
    # Simplified port memory estimation
    # Assume 1KB per port
    length(ports) * 1024
  end

  # Leak detection functions
  defp capture_memory_state do
    %{
      timestamp: System.monotonic_time(:millisecond),
      memory: :erlang.memory(),
      process_count: length(Process.list()),
      ets_count: length(:ets.all()),
      port_count: length(Port.list()),
      top_processes:
        Process.list() |> Enum.take(10) |> Enum.map(&get_process_memory_info/1)
    }
  end

  defp monitor_memory_growth(duration, initial_state) do
    measurements = []

    monitor_loop(
      duration,
      initial_state,
      measurements,
      System.monotonic_time(:millisecond)
    )
  end

  defp monitor_loop(duration, initial_state, measurements, start_time) do
    current_time = System.monotonic_time(:millisecond)
    elapsed = (current_time - start_time) / 1000

    if elapsed < duration do
      state = capture_memory_state()

      measurement = %{
        elapsed: elapsed,
        state: state,
        growth: calculate_growth(initial_state, state)
      }

      updated_measurements = [measurement | measurements]

      # Progress update every 30 seconds
      if rem(trunc(elapsed), 30) == 0 and elapsed > 0 do
        Mix.shell().info(
          "Monitoring... #{trunc(elapsed)}s elapsed, Memory: #{format_memory(state.memory[:total])}"
        )
      end

      # Check every 5 seconds
      Process.sleep(5000)
      monitor_loop(duration, initial_state, updated_measurements, start_time)
    else
      Enum.reverse(measurements)
    end
  end

  defp calculate_growth(initial, current) do
    %{
      memory_growth: current.memory[:total] - initial.memory[:total],
      process_growth: current.process_count - initial.process_count,
      ets_growth: current.ets_count - initial.ets_count,
      port_growth: current.port_count - initial.port_count
    }
  end

  defp analyze_potential_leaks(_initial_state, measurements, config) do
    if length(measurements) < 2 do
      %{
        status: :insufficient_data,
        message: "Not enough data for leak analysis"
      }
    else
      final_measurement = List.last(measurements)
      total_growth = final_measurement.growth

      leak_indicators = []

      # Check for memory growth
      memory_growth_mb = total_growth.memory_growth / 1_000_000

      leak_indicators =
        if memory_growth_mb > config.threshold * 2 do
          [
            %{
              type: :memory_leak,
              severity: :high,
              growth: memory_growth_mb,
              unit: "MB"
            }
            | leak_indicators
          ]
        else
          leak_indicators
        end

      # Check for process growth
      leak_indicators =
        if total_growth.process_growth > 10 do
          [
            %{
              type: :process_leak,
              severity: :medium,
              growth: total_growth.process_growth,
              unit: "processes"
            }
            | leak_indicators
          ]
        else
          leak_indicators
        end

      # Check for ETS table growth
      leak_indicators =
        if total_growth.ets_growth > 5 do
          [
            %{
              type: :ets_leak,
              severity: :medium,
              growth: total_growth.ets_growth,
              unit: "tables"
            }
            | leak_indicators
          ]
        else
          leak_indicators
        end

      trend_analysis = analyze_growth_trend(measurements)

      %{
        status:
          if(length(leak_indicators) > 0, do: :leaks_detected, else: :no_leaks),
        monitoring_duration: config.monitoring_duration,
        total_growth: total_growth,
        leak_indicators: leak_indicators,
        trend_analysis: trend_analysis,
        recommendations: generate_leak_recommendations(leak_indicators)
      }
    end
  end

  defp analyze_growth_trend(measurements) do
    # Analyze if growth is linear, exponential, or stabilizing
    growth_rates =
      measurements
      |> Enum.chunk_every(2, 1, :discard)
      |> Enum.map(fn [prev, curr] ->
        curr.growth.memory_growth - prev.growth.memory_growth
      end)

    avg_growth_rate =
      if length(growth_rates) > 0 do
        Enum.sum(growth_rates) / length(growth_rates)
      else
        0
      end

    %{
      trend: determine_trend(growth_rates),
      average_growth_rate: avg_growth_rate,
      stability: calculate_stability(growth_rates)
    }
  end

  defp determine_trend(growth_rates) do
    if length(growth_rates) < 3 do
      :unknown
    else
      recent = Enum.take(growth_rates, -3)
      if Enum.all?(recent, &(&1 > 0)), do: :increasing, else: :stable
    end
  end

  defp calculate_stability(growth_rates) do
    if length(growth_rates) < 2 do
      :unknown
    else
      variance = calculate_variance(growth_rates)
      if variance < 1000, do: :stable, else: :unstable
    end
  end

  defp calculate_variance(values) do
    mean = Enum.sum(values) / length(values)
    sum_squares = Enum.sum(Enum.map(values, &:math.pow(&1 - mean, 2)))
    sum_squares / length(values)
  end

  defp generate_leak_recommendations(leak_indicators) do
    leak_indicators
    |> Enum.flat_map(&get_recommendations_for_leak_type/1)
    |> Enum.uniq()
  end

  defp get_recommendations_for_leak_type(%{type: :memory_leak}) do
    [
      "Monitor process memory growth over time",
      "Check for large binary accumulation",
      "Review ETS table usage patterns",
      "Ensure proper cleanup of resources"
    ]
  end

  defp get_recommendations_for_leak_type(%{type: :process_leak}) do
    [
      "Review process spawning patterns",
      "Ensure processes are properly terminated",
      "Check for supervisor restart loops",
      "Monitor GenServer lifecycle"
    ]
  end

  defp get_recommendations_for_leak_type(%{type: :ets_leak}) do
    [
      "Review ETS table creation and deletion",
      "Ensure tables are properly cleaned up",
      "Check for orphaned ETS tables",
      "Monitor table ownership transfers"
    ]
  end

  # Optimization analysis functions
  defp analyze_configuration_optimizations do
    vm_args = get_vm_args()

    recommendations = []

    # Check heap size configuration
    recommendations =
      if should_recommend_heap_tuning(vm_args) do
        [
          "Consider tuning heap sizes for better memory efficiency"
          | recommendations
        ]
      else
        recommendations
      end

    # Check GC configuration
    recommendations =
      if should_recommend_gc_tuning() do
        ["Consider adjusting garbage collection parameters" | recommendations]
      else
        recommendations
      end

    %{
      current_config: vm_args,
      recommendations: recommendations
    }
  end

  defp get_vm_args do
    # This would parse actual VM arguments
    # Simplified for demonstration
    %{
      heap_size: :erlang.system_info(:heap_type),
      schedulers: :erlang.system_info(:schedulers),
      async_threads: :erlang.system_info(:thread_pool_size)
    }
  end

  defp should_recommend_heap_tuning(_vm_args) do
    # Check if heap tuning might help
    memory = :erlang.memory()
    # More than 60% in processes
    memory[:processes] > memory[:total] * 0.6
  end

  defp should_recommend_gc_tuning do
    # Check GC statistics to see if tuning might help
    {_gc_count, _words_reclaimed, _reductions} =
      :erlang.statistics(:garbage_collection)

    # Simplified check
    false
  end

  defp analyze_process_optimizations(_config) do
    long_queues = find_processes_with_long_queues()
    large_heaps = find_processes_with_large_heaps()

    %{
      long_message_queues: long_queues,
      large_heap_processes: large_heaps,
      recommendations:
        generate_process_recommendations(long_queues, large_heaps)
    }
  end

  defp find_processes_with_long_queues do
    Process.list()
    |> Enum.map(fn pid ->
      case Process.info(pid, [:message_queue_len, :registered_name]) do
        nil ->
          nil

        [message_queue_len: len, registered_name: name] when len > 1000 ->
          %{pid: pid, name: name, queue_length: len}

        _ ->
          nil
      end
    end)
    |> Enum.reject(&is_nil/1)
  end

  defp find_processes_with_large_heaps do
    Process.list()
    |> Enum.map(fn pid ->
      case Process.info(pid, [:heap_size, :registered_name]) do
        nil ->
          nil

        [heap_size: size, registered_name: name] when size > 100_000 ->
          %{pid: pid, name: name, heap_size: size}

        _ ->
          nil
      end
    end)
    |> Enum.reject(&is_nil/1)
  end

  defp generate_process_recommendations(long_queues, large_heaps) do
    recommendations = []

    recommendations =
      if length(long_queues) > 0 do
        [
          "Consider implementing backpressure for processes with long queues"
          | recommendations
        ]
      else
        recommendations
      end

    recommendations =
      if length(large_heaps) > 0 do
        [
          "Review processes with large heaps for memory optimization"
          | recommendations
        ]
      else
        recommendations
      end

    if Enum.empty?(recommendations) do
      ["Process memory usage appears optimal"]
    else
      recommendations
    end
  end

  defp analyze_binary_optimizations do
    %{
      current_usage: :erlang.memory(:binary),
      recommendations: [
        "Use iodata instead of string concatenation",
        "Consider binary streaming for large data",
        "Use binary comprehensions where appropriate",
        "Avoid unnecessary binary copying"
      ]
    }
  end

  defp analyze_ets_optimizations(config) do
    tables = analyze_ets_tables(config)

    %{
      table_count: tables.total_count,
      total_memory: tables.total_memory,
      recommendations: [
        "Consider using ordered_set for sorted data",
        "Use read_concurrency for read-heavy tables",
        "Use write_concurrency for write-heavy tables",
        "Consider table partitioning for very large tables"
      ]
    }
  end

  defp analyze_gc_optimizations do
    {gc_count, words_reclaimed, _} = :erlang.statistics(:garbage_collection)

    %{
      collections: gc_count,
      words_reclaimed: words_reclaimed,
      recommendations: [
        "Monitor GC frequency and tune if needed",
        "Consider fullsweep_after tuning for long-lived processes",
        "Use hibernation for idle processes",
        "Avoid creating many short-lived large terms"
      ]
    }
  end

  defp get_general_recommendations do
    [
      "Regular memory profiling helps identify issues early",
      "Use memory monitoring in production",
      "Implement proper resource cleanup",
      "Consider using supervision trees for fault tolerance",
      "Monitor memory trends over time",
      "Use appropriate data structures for your use case",
      "Profile before optimizing",
      "Test memory usage under load"
    ]
  end

  # Output formatting functions
  defp format_and_output_analysis(analysis, config) do
    case config.format do
      "json" -> output_json(analysis, config)
      "markdown" -> output_markdown_analysis(analysis, config)
      _ -> output_text_analysis(analysis, config)
    end
  end

  defp format_and_output_hotspots(hotspots, config) do
    case config.format do
      "json" -> output_json(hotspots, config)
      "markdown" -> output_markdown_hotspots(hotspots, config)
      _ -> output_text_hotspots(hotspots, config)
    end
  end

  defp format_and_output_leaks(leak_analysis, config) do
    case config.format do
      "json" -> output_json(leak_analysis, config)
      "markdown" -> output_markdown_leaks(leak_analysis, config)
      _ -> output_text_leaks(leak_analysis, config)
    end
  end

  defp format_and_output_optimizations(optimizations, config) do
    case config.format do
      "json" -> output_json(optimizations, config)
      "markdown" -> output_markdown_optimizations(optimizations, config)
      _ -> output_text_optimizations(optimizations, config)
    end
  end

  defp output_text_analysis(analysis, config) do
    Mix.shell().info("\nMemory Analysis Report")
    Mix.shell().info(String.duplicate("=", 50))

    # Memory overview
    memory = analysis.memory_overview
    Mix.shell().info("\nMemory Overview:")
    Mix.shell().info("  Total: #{format_memory(memory.total)}")

    Mix.shell().info(
      "  Processes: #{format_memory(memory.processes)} (#{memory.breakdown.processes.percentage}%)"
    )

    Mix.shell().info(
      "  System: #{format_memory(memory.system)} (#{memory.breakdown.system.percentage}%)"
    )

    Mix.shell().info(
      "  Binary: #{format_memory(memory.binary)} (#{memory.breakdown.binary.percentage}%)"
    )

    # Top processes
    Mix.shell().info("\nTop Memory Consuming Processes:")

    Enum.take(analysis.process_analysis.top_consumers, 5)
    |> Enum.each(fn proc ->
      Mix.shell().info("  #{proc.name}: #{format_memory(proc.memory)}")
    end)

    # ETS tables
    if length(analysis.ets_analysis.top_consumers) > 0 do
      Mix.shell().info("\nLargest ETS Tables:")

      Enum.take(analysis.ets_analysis.top_consumers, 3)
      |> Enum.each(fn table ->
        Mix.shell().info(
          "  #{table.name}: #{format_memory(table.memory)} (#{table.size} entries)"
        )
      end)
    end

    save_output_if_requested(analysis, config)
  end

  defp output_text_hotspots(hotspots, config) do
    Mix.shell().info("\nMemory Hotspots Report")
    Mix.shell().info(String.duplicate("=", 50))

    Mix.shell().info("\nTop Memory Consuming Processes:")

    Enum.take(hotspots.top_processes, 10)
    |> Enum.each(fn proc ->
      Mix.shell().info("  #{proc.name}: #{format_memory(proc.memory)}")
    end)

    Mix.shell().info("\nBinary Memory Hotspots:")

    Enum.each(hotspots.binary_hotspots, fn hotspot ->
      Mix.shell().info(
        "  #{hotspot.process}: #{format_memory(hotspot.binary_memory)} - #{hotspot.recommendation}"
      )
    end)

    save_output_if_requested(hotspots, config)
  end

  defp output_text_leaks(leak_analysis, config) do
    Mix.shell().info("\nMemory Leak Detection Report")
    Mix.shell().info(String.duplicate("=", 50))

    case leak_analysis.status do
      :insufficient_data ->
        Mix.shell().info("Insufficient data for leak analysis")

      :no_leaks ->
        Mix.shell().info("No memory leaks detected")

        Mix.shell().info(
          "Memory growth: #{format_memory(leak_analysis.total_growth.memory_growth)}"
        )

      :leaks_detected ->
        Mix.shell().info("MEMORY LEAKS DETECTED!")

        Mix.shell().info(
          "Total memory growth: #{format_memory(leak_analysis.total_growth.memory_growth)}"
        )

        Enum.each(leak_analysis.leak_indicators, fn indicator ->
          Mix.shell().info(
            "  #{indicator.type}: +#{indicator.growth} #{indicator.unit} (#{indicator.severity})"
          )
        end)

        Mix.shell().info("\nRecommendations:")

        Enum.each(leak_analysis.recommendations, fn rec ->
          Mix.shell().info("  - #{rec}")
        end)
    end

    save_output_if_requested(leak_analysis, config)
  end

  defp output_text_optimizations(optimizations, config) do
    Mix.shell().info("\nMemory Optimization Report")
    Mix.shell().info(String.duplicate("=", 50))

    Mix.shell().info("\nGeneral Recommendations:")

    Enum.each(optimizations.general_recommendations, fn rec ->
      Mix.shell().info("  - #{rec}")
    end)

    Mix.shell().info("\nBinary Optimizations:")

    Enum.each(optimizations.binary_optimizations.recommendations, fn rec ->
      Mix.shell().info("  - #{rec}")
    end)

    Mix.shell().info("\nProcess Optimizations:")

    Enum.each(optimizations.process_optimizations.recommendations, fn rec ->
      Mix.shell().info("  - #{rec}")
    end)

    save_output_if_requested(optimizations, config)
  end

  defp output_json(data, config) do
    json = Jason.encode!(data, pretty: true)
    Log.info(json)
    save_output_if_requested(data, config)
  end

  defp output_markdown_analysis(analysis, config) do
    # Simplified markdown output - would be more comprehensive in real implementation
    markdown = """
    # Memory Analysis Report

    Generated: #{analysis.timestamp}

    ## Memory Overview

    - **Total**: #{format_memory(analysis.memory_overview.total)}
    - **Processes**: #{format_memory(analysis.memory_overview.processes)}
    - **System**: #{format_memory(analysis.memory_overview.system)}
    - **Binary**: #{format_memory(analysis.memory_overview.binary)}

    ## Top Processes

    #{format_processes_table(analysis.process_analysis.top_consumers)}
    """

    Log.info(markdown)
    save_output_if_requested(markdown, config)
  end

  defp output_markdown_hotspots(_hotspots, config) do
    Mix.shell().info("Markdown hotspots output not yet implemented")
    save_output_if_requested("", config)
  end

  defp output_markdown_leaks(_leak_analysis, config) do
    Mix.shell().info("Markdown leaks output not yet implemented")
    save_output_if_requested("", config)
  end

  defp output_markdown_optimizations(_optimizations, config) do
    Mix.shell().info("Markdown optimizations output not yet implemented")
    save_output_if_requested("", config)
  end

  defp format_processes_table(processes) do
    processes
    |> Enum.take(5)
    |> Enum.map_join("\n", fn proc ->
      "| #{proc.name} | #{format_memory(proc.memory)} |"
    end)
  end

  defp save_output_if_requested(data, config) do
    if config.output do
      content =
        case config.format do
          "json" -> Jason.encode!(data, pretty: true)
          _ -> inspect(data, pretty: true)
        end

      File.write!(config.output, content)
      Mix.shell().info("Results saved to: #{config.output}")
    end
  end

  defp format_memory(bytes), do: MemoryFormatter.format_memory(bytes)

  defp print_help do
    Mix.shell().info(@moduledoc)
  end
end
