defmodule Mix.Tasks.Raxol.Bench do
  @moduledoc """
  Enhanced benchmarking task for Raxol terminal emulator.

  Provides comprehensive performance benchmarking with multiple
  output formats, regression detection, and dashboard generation.

  ## Usage

      mix raxol.bench                    # Run all benchmarks
      mix raxol.bench parser             # Run parser benchmarks only
      mix raxol.bench terminal           # Run terminal component benchmarks
      mix raxol.bench rendering          # Run rendering benchmarks only
      mix raxol.bench memory             # Run memory benchmarks only
      mix raxol.bench dashboard          # Generate dashboard from latest results

  ## Options

    * `--quick`       - Quick benchmark run with reduced time
    * `--compare`     - Compare with previous benchmark results
    * `--dashboard`   - Generate full performance dashboard with charts
    * `--regression`  - Check for performance regressions (5% threshold)
    * `--help`        - Show detailed help

  ## Examples

      # Quick performance check
      mix raxol.bench --quick

      # Full benchmarks with regression detection
      mix raxol.bench --regression

      # Parser benchmarks with dashboard generation
      mix raxol.bench parser --dashboard

      # Compare current run with previous results
      mix raxol.bench --compare

  ## Output

  Results are saved to `bench/output/enhanced/` with:
    - HTML reports for interactive viewing
    - JSON data for programmatic analysis
    - Performance dashboard with charts
    - Regression analysis reports
    - Baseline metrics for future comparison
  """

  use Mix.Task
  require Logger

  @shortdoc "Run enhanced performance benchmarks"

  @compile {:no_warn_undefined, Raxol.Bench.Dashboard}
  @compile {:no_warn_undefined, Raxol.Bench.TestData}
  @compile {:no_warn_undefined, Benchee}

  alias Raxol.Bench.{Dashboard, TestData}

  @switches [
    quick: :boolean,
    compare: :boolean,
    dashboard: :boolean,
    regression: :boolean,
    help: :boolean,
    format: :string,
    output: :string,
    suite: :string
  ]

  def run(args) do
    {opts, args, _} = OptionParser.parse(args, switches: @switches)

    if opts[:help] do
      print_help()
    else
      Mix.Task.run("app.start")
      execute_benchmark(args, opts)
    end
  end

  defp execute_benchmark(args, opts) do
    # Handle --suite flag as primary routing for new framework suites
    if suite = opts[:suite] do
      run_framework_suite(suite, opts)
    else
      execute_benchmark_by_arg(args, opts)
    end
  end

  @framework_suites ~w(render latency memory_widgets startup comparison)

  defp execute_benchmark_by_arg([], opts), do: run_all_benchmarks(opts)

  defp execute_benchmark_by_arg([suite], opts) when suite in @framework_suites,
    do: run_framework_suite(suite, opts)

  defp execute_benchmark_by_arg(["parser"], opts), do: run_parser_only(opts)
  defp execute_benchmark_by_arg(["terminal"], opts), do: run_terminal_only(opts)

  defp execute_benchmark_by_arg(["rendering"], opts),
    do: run_rendering_only(opts)

  defp execute_benchmark_by_arg(["memory"], opts), do: run_memory_only(opts)

  defp execute_benchmark_by_arg(["dashboard"], opts),
    do: run_dashboard_only(opts)

  defp execute_benchmark_by_arg([benchmark], _opts) do
    Mix.shell().error("Unknown benchmark: #{benchmark}")
    print_help()
  end

  defp run_all_benchmarks(opts) do
    Mix.shell().info("Running Raxol Performance Benchmarks...")

    if opts[:quick] do
      run_quick_benchmarks()
    else
      run_comprehensive_benchmarks(opts)
    end
  end

  defp run_quick_benchmarks do
    Mix.shell().info("Quick benchmark mode (reduced time)")

    benchmark_config = [
      time: 1,
      memory_time: 0.5,
      warmup: 0.2,
      formatters: [Benchee.Formatters.Console]
    ]

    _ = run_parser_benchmarks(benchmark_config, quick: true)
    _ = run_terminal_benchmarks(benchmark_config, quick: true)
  end

  defp run_comprehensive_benchmarks(opts) do
    Mix.shell().info("Running comprehensive benchmarks...")

    File.mkdir_p!("bench/output/enhanced")
    File.mkdir_p!("bench/output/enhanced/json")
    File.mkdir_p!("bench/output/enhanced/html")

    timestamp = DateTime.utc_now() |> DateTime.to_iso8601()

    results =
      %{}
      |> Map.put(:parser, run_parser_benchmarks(standard_config(timestamp)))
      |> Map.put(:terminal, run_terminal_benchmarks(standard_config(timestamp)))
      |> Map.put(
        :rendering,
        run_rendering_benchmarks(standard_config(timestamp))
      )
      |> Map.put(:memory, run_memory_benchmarks(standard_config(timestamp)))
      |> Map.put(
        :concurrent,
        run_concurrent_benchmarks(standard_config(timestamp))
      )

    if opts[:regression], do: Dashboard.check_for_regressions(results)

    if opts[:dashboard],
      do: Dashboard.generate_enhanced_dashboard(results, timestamp)

    if opts[:compare], do: Dashboard.run_comparison_analysis(results, timestamp)

    Dashboard.print_results_summary(results, timestamp)
  end

  defp run_parser_only(opts) do
    Mix.shell().info("Running Parser Benchmarks Only...")
    config = if opts[:quick], do: quick_config(), else: standard_config()
    run_parser_benchmarks(config)
  end

  defp run_terminal_only(opts) do
    Mix.shell().info("Running Terminal Component Benchmarks Only...")
    config = if opts[:quick], do: quick_config(), else: standard_config()
    run_terminal_benchmarks(config)
  end

  defp run_rendering_only(opts) do
    Mix.shell().info("Running Rendering Benchmarks Only...")
    config = if opts[:quick], do: quick_config(), else: standard_config()
    run_rendering_benchmarks(config)
  end

  defp run_memory_only(opts) do
    Mix.shell().info("Running Memory Benchmarks Only...")
    config = if opts[:quick], do: quick_config(), else: standard_config()
    run_memory_benchmarks(config)
  end

  defp run_dashboard_only(_opts) do
    Mix.shell().info("Generating Performance Dashboard...")
    timestamp = DateTime.utc_now() |> DateTime.to_iso8601()
    results = Dashboard.load_latest_results() || %{}
    Dashboard.generate_enhanced_dashboard(results, timestamp)

    Mix.shell().info(
      "Dashboard generated at bench/output/enhanced/dashboard.html"
    )
  end

  defp run_parser_benchmarks(config, opts \\ []) do
    alias Raxol.Terminal.ANSI.StateMachine
    alias Raxol.Terminal.ANSI.Utils.AnsiParser
    alias Raxol.Terminal.Emulator

    _emulator = Emulator.new(80, 24)

    scenarios =
      if opts[:quick] do
        %{
          "plain_text" => "Hello, World!",
          "ansi_basic" => "\e[31mRed Text\e[0m",
          "ansi_complex" => "\e[1;4;31mBold Red Underlined\e[0m"
        }
      else
        TestData.full_parser_scenarios()
      end

    jobs =
      Enum.into(scenarios, %{}, fn {name, content} ->
        {name,
         fn ->
           state_machine = StateMachine.new()
           AnsiParser.parse(state_machine, content)
         end}
      end)

    Benchee.run(jobs, config)
  end

  defp run_terminal_benchmarks(config, opts \\ []) do
    jobs =
      if opts[:quick],
        do: TestData.terminal_quick_jobs(),
        else: TestData.terminal_comprehensive_jobs()

    Benchee.run(jobs, config)
  end

  defp run_rendering_benchmarks(config) do
    alias Raxol.UI.Rendering.RenderBatcher

    {_small_buffer, medium_buffer, large_buffer} =
      TestData.create_test_buffers()

    jobs = %{
      "render_with_damage_tracking" => fn ->
        RenderBatcher.batch_render(medium_buffer,
          changed_regions: [{0, 0, 10, 10}]
        )
      end,
      "render_full_redraw" => fn ->
        RenderBatcher.batch_render(large_buffer, full_redraw: true)
      end
    }

    Benchee.run(jobs, config)
  end

  defp run_memory_benchmarks(config) do
    Benchee.run(memory_benchmark_jobs(), config)
  end

  defp memory_benchmark_jobs do
    alias Raxol.Terminal.ANSI.StateMachine
    alias Raxol.Terminal.ANSI.Utils.AnsiParser
    alias Raxol.Terminal.Cursor
    alias Raxol.Terminal.Emulator
    alias Raxol.Terminal.ScreenBufferAdapter, as: ScreenBuffer

    %{
      "memory_emulator_80x24" => fn -> Emulator.new(80, 24) end,
      "memory_emulator_200x50" => fn -> Emulator.new(200, 50) end,
      "memory_parser_session" => fn ->
        emulator = Emulator.new(80, 24)
        state_machine = StateMachine.new()
        content = TestData.generate_test_content()
        _ = AnsiParser.parse(state_machine, content)
        {emulator, state_machine}
      end,
      "memory_buffer_operations" => fn ->
        buffer = ScreenBuffer.new(100, 30)
        _cursor = Cursor.new()

        Enum.reduce(1..50, buffer, fn i, acc ->
          ScreenBuffer.write_string(acc, 0, rem(i, 30), "Test line #{i}")
        end)
      end,
      "memory_large_buffer_allocation" => fn -> ScreenBuffer.new(500, 500) end,
      "memory_plugin_system" => fn ->
        alias Raxol.Plugins.Manager
        {:ok, manager} = Manager.new()
        Manager.list_plugins(manager)
      end
    }
  end

  defp run_concurrent_benchmarks(config) do
    jobs = %{
      "concurrent_emulator_creation" => &bench_emulator_creation/0,
      "concurrent_buffer_writes" => &bench_buffer_writes/0,
      "concurrent_event_dispatch" => &bench_event_dispatch/0,
      "concurrent_plugin_operations" => &bench_plugin_operations/0
    }

    Benchee.run(jobs, config)
  end

  defp bench_emulator_creation do
    alias Raxol.Terminal.Emulator

    1..10
    |> Enum.map(fn _ -> Task.async(fn -> Emulator.new(80, 24) end) end)
    |> Task.await_many()
  end

  defp bench_buffer_writes do
    alias Raxol.Terminal.ScreenBufferAdapter, as: ScreenBuffer
    buffer = ScreenBuffer.new(80, 24)

    1..10
    |> Enum.map(fn i ->
      Task.async(fn -> ScreenBuffer.write_char(buffer, i, i, "X") end)
    end)
    |> Task.await_many()
  end

  defp bench_event_dispatch do
    alias Raxol.Core.Events.EventManager

    case EventManager.start_link(name: EventManager) do
      {:ok, _pid} -> :ok
      {:error, {:already_started, _pid}} -> :ok
    end

    1..20
    |> Enum.map(fn i ->
      Task.async(fn -> EventManager.dispatch(:test_event, %{id: i}) end)
    end)
    |> Task.await_many()
  end

  defp bench_plugin_operations do
    alias Raxol.Plugins.Manager

    {:ok, manager} = Manager.new()

    1..5
    |> Enum.map(fn _ ->
      Task.async(fn ->
        _plugins = Manager.list_plugins(manager)
        Manager.get_plugin_state(manager, "test_plugin")
      end)
    end)
    |> Task.await_many()
  end

  defp standard_config(timestamp \\ nil) do
    ts = timestamp || DateTime.utc_now() |> DateTime.to_iso8601()

    [
      time: 5,
      memory_time: 2,
      warmup: 1,
      formatters: [
        Benchee.Formatters.Console,
        {Benchee.Formatters.JSON,
         file: "bench/output/enhanced/json/benchmark_#{ts}.json"},
        {Benchee.Formatters.HTML,
         file: "bench/output/enhanced/html/benchmark_#{ts}.html"}
      ],
      save: [path: "bench/output/enhanced/benchee_#{ts}.benchee"],
      load: "bench/output/enhanced/*.benchee"
    ]
  end

  defp quick_config do
    [
      time: 1,
      memory_time: 0.5,
      warmup: 0.2,
      formatters: [Benchee.Formatters.Console]
    ]
  end

  # -- Framework-level suites --

  @compile {:no_warn_undefined, Raxol.Benchmark.Formatter}

  defp run_framework_suite(suite, opts) do
    quick = opts[:quick] || false

    case resolve_suite(suite, quick) do
      {:run, jobs, config} ->
        prev_level = Logger.level()
        if suite == "startup", do: Logger.configure(level: :error)

        result = Benchee.run(jobs, config)

        if suite == "startup", do: Logger.configure(level: prev_level)
        maybe_write_output(result, suite, opts)

      :skip ->
        handle_non_benchee_suite(suite, opts)
    end
  end

  defp resolve_suite(suite, quick) do
    config = if quick, do: quick_config(), else: framework_config()

    case suite do
      "render" ->
        {:run, Raxol.Benchmark.Suites.RenderThroughput.jobs(quick: quick),
         config}

      "latency" ->
        {:run, Raxol.Benchmark.Suites.InputLatency.jobs(quick: quick), config}

      "memory_widgets" ->
        config = Keyword.put(config, :memory_time, 2)
        {:run, Raxol.Benchmark.Suites.WidgetMemory.jobs(quick: quick), config}

      "startup" ->
        {:run, Raxol.Benchmark.Suites.Startup.jobs(quick: quick), config}

      _ ->
        :skip
    end
  end

  defp handle_non_benchee_suite("comparison", _opts),
    do: print_comparison_table()

  defp handle_non_benchee_suite("all", opts), do: run_all_framework_suites(opts)

  defp handle_non_benchee_suite(other, _opts) do
    Mix.shell().error("Unknown suite: #{other}")
  end

  defp run_all_framework_suites(opts) do
    for suite <- ~w(render latency memory_widgets startup) do
      Mix.shell().info("\n--- Suite: #{suite} ---")
      run_framework_suite(suite, opts)
    end

    print_comparison_table()
  end

  defp print_comparison_table do
    Mix.shell().info("\n=== COMPETITOR COMPARISON (static reference) ===")

    shell = Mix.shell()

    Raxol.Benchmark.Suites.Comparison.comparison_table()
    |> Enum.each(fn line -> shell.info(line) end)

    Mix.shell().info(
      "\nNote: Competitor numbers are published estimates. Raxol numbers are measured above."
    )
  end

  defp maybe_write_output(result, suite, opts) do
    case opts[:format] do
      nil ->
        :ok

      format ->
        write_formatted_output(format, opts[:output], %{suite => result})
    end
  end

  defp write_formatted_output("json", path, data) when is_binary(path) do
    content = Raxol.Benchmark.Formatter.json(data, include_system: true)
    :ok = Raxol.Benchmark.Formatter.write(content, path)
    Mix.shell().info("JSON results written to #{path}")
  end

  defp write_formatted_output("markdown", path, data) when is_binary(path) do
    content = Raxol.Benchmark.Formatter.markdown(data)
    :ok = Raxol.Benchmark.Formatter.write(content, path)
    Mix.shell().info("Markdown results written to #{path}")
  end

  defp write_formatted_output("json", nil, data),
    do: IO.puts(Raxol.Benchmark.Formatter.json(data))

  defp write_formatted_output("markdown", nil, data),
    do: IO.puts(Raxol.Benchmark.Formatter.markdown(data))

  defp write_formatted_output(_format, _path, _data), do: :ok

  defp framework_config do
    [
      time: 3,
      memory_time: 1,
      warmup: 0.5,
      formatters: [Benchee.Formatters.Console]
    ]
  end

  defp print_help do
    Mix.shell().info("""

    Raxol Enhanced Benchmarking Tool

    Usage:
      mix raxol.bench                    # Run all benchmarks
      mix raxol.bench render             # Framework render throughput
      mix raxol.bench latency            # Framework input latency
      mix raxol.bench memory_widgets     # Widget memory profiling
      mix raxol.bench startup            # Lifecycle startup timing
      mix raxol.bench comparison         # Show competitor reference data
      mix raxol.bench parser             # Legacy: parser benchmarks
      mix raxol.bench terminal           # Legacy: terminal benchmarks
      mix raxol.bench rendering          # Legacy: rendering benchmarks
      mix raxol.bench memory             # Legacy: memory benchmarks
      mix raxol.bench dashboard          # Generate dashboard from latest results

    Options:
      --suite NAME                       # Run a specific suite (render, latency, etc.)
      --quick                            # Quick benchmark run (reduced time)
      --format FORMAT                    # Output format: json, markdown
      --output PATH                      # Write formatted output to file
      --compare                          # Compare with previous results
      --dashboard                        # Generate full performance dashboard
      --regression                       # Check for performance regressions (5% threshold)
      --help                             # Show this help

    Examples:
      mix raxol.bench --quick            # Quick performance check
      mix raxol.bench render             # Render throughput only
      mix raxol.bench --suite render --format markdown --output BENCHMARKS.md
      mix raxol.bench parser --dashboard # Parser benchmarks + dashboard
      mix raxol.bench --regression       # Full benchmarks with regression detection

    Output:
      Results are saved to bench/output/enhanced/ with:
      * HTML reports for interactive viewing
      * JSON data for programmatic analysis
      * Performance dashboard with charts
      * Regression analysis reports
      * Baseline metrics for comparison
    """)
  end
end
