defmodule Mix.Tasks.Raxol.Perf.Monitor do
  @moduledoc """
  Automated performance monitoring task for Raxol.

  This task provides comprehensive performance monitoring capabilities:
  - Start/stop continuous monitoring
  - View real-time performance metrics
  - Check for performance regressions
  - Configure alert thresholds
  - Generate performance reports

  ## Usage

      # Start monitoring with default settings
      mix raxol.perf.monitor start

      # Start with custom thresholds
      mix raxol.perf.monitor start --render-threshold 20 --memory-threshold 100

      # View current status
      mix raxol.perf.monitor status

      # Check for regressions
      mix raxol.perf.monitor regression-check

      # Generate performance report
      mix raxol.perf.monitor report --output performance_report.json

      # Stop monitoring
      mix raxol.perf.monitor stop

  ## Options

    * `--render-threshold` - Maximum average render time in ms (default: 16.67)
    * `--memory-threshold` - Maximum memory usage in MB (default: 50)
    * `--parse-threshold` - Maximum parse time in microseconds (default: 500)
    * `--error-threshold` - Maximum error rate percentage (default: 1.0)
    * `--output` - Output file for reports (default: stdout)
    * `--format` - Report format: json, csv, or text (default: text)
  """

  use Mix.Task
  alias Raxol.Core.Runtime.Log
  alias Raxol.Performance.AutomatedMonitor

  @shortdoc "Automated performance monitoring for Raxol"

  @impl Mix.Task
  def run(args) do
    # Ensure the application is started
    Mix.Task.run("app.start")

    {opts, commands, _} =
      OptionParser.parse(args,
        switches: [
          render_threshold: :float,
          memory_threshold: :float,
          parse_threshold: :float,
          error_threshold: :float,
          output: :string,
          format: :string,
          help: :boolean
        ],
        aliases: [
          h: :help,
          o: :output,
          f: :format
        ]
      )

    if opts[:help] || commands == [] do
      print_help()
    else
      execute_command(commands, opts)
    end
  end

  defp execute_command(["start"], opts) do
    thresholds = build_thresholds_from_opts(opts)

    Log.console("Starting automated performance monitoring...")
    Log.console("Thresholds: #{inspect(thresholds)}")

    case AutomatedMonitor.start_monitoring(thresholds: thresholds) do
      :ok ->
        Log.console("✓ Performance monitoring started successfully")
        Log.console("Monitor will collect metrics every 30 seconds")

        Log.console(
          "Use 'mix raxol.perf.monitor status' to view current metrics"
        )

      {:already_running, current_metrics} ->
        Log.console("⚠ Performance monitoring is already running")
        print_current_metrics(current_metrics)

      {:error, reason} ->
        Log.error(
          "✗ Failed to start performance monitoring: #{inspect(reason)}"
        )

        System.halt(1)
    end
  end

  defp execute_command(["stop"], _opts) do
    Log.console("Stopping automated performance monitoring...")

    case AutomatedMonitor.stop_monitoring() do
      :ok ->
        Log.console("✓ Performance monitoring stopped")

      {:error, reason} ->
        Log.error("✗ Failed to stop performance monitoring: #{inspect(reason)}")
        System.halt(1)
    end
  end

  defp execute_command(["status"], _opts) do
    case AutomatedMonitor.get_status() do
      %{monitoring_enabled: false} ->
        Log.console("Performance monitoring is not running")
        Log.console("Use 'mix raxol.perf.monitor start' to begin monitoring")

      status ->
        Log.console("=== Performance Monitoring Status ===")

        Log.console(
          "Status: #{if status.monitoring_enabled, do: "ACTIVE", else: "INACTIVE"}"
        )

        Log.console("Active alerts: #{status.alert_count}")
        Log.console("")

        print_current_metrics(status.current_metrics)
        print_threshold_status(status.current_metrics, status.thresholds)
        print_recommendations(status.recommendations)
    end
  end

  defp execute_command(["regression-check"], _opts) do
    Log.console("Checking for performance regressions...")

    case AutomatedMonitor.check_regressions() do
      {:ok, []} ->
        Log.console("✓ No performance regressions detected")

      {:ok, regressions} ->
        Log.console("⚠ Performance regressions detected:")
        Enum.each(regressions, &print_regression/1)

      {:error, :insufficient_data} ->
        Log.console("⚠ Insufficient data for regression analysis")

        Log.console(
          "Run monitoring for at least 5 minutes to collect baseline data"
        )

      {:error, reason} ->
        Log.error("✗ Regression check failed: #{inspect(reason)}")
        System.halt(1)
    end
  end

  defp execute_command(["report"], opts) do
    format = Keyword.get(opts, :format, "text")
    output_file = Keyword.get(opts, :output)

    Log.console("Generating performance report (format: #{format})...")

    case AutomatedMonitor.get_status() do
      %{monitoring_enabled: false} ->
        Log.console(
          "⚠ No monitoring data available - monitoring is not running"
        )

        System.halt(1)

      status ->
        report = generate_performance_report(status, format)

        case output_file do
          nil ->
            Log.console(report)

          file_path ->
            File.write!(file_path, report)
            Log.console("✓ Performance report written to #{file_path}")
        end
    end
  end

  defp execute_command(["alerts"], _opts) do
    case AutomatedMonitor.get_alerts() do
      alerts when map_size(alerts) == 0 ->
        Log.console("✓ No active performance alerts")

      alerts ->
        Log.console("=== Active Performance Alerts ===")

        Enum.each(alerts, fn {alert_key, alert_data} ->
          violation = alert_data.violation
          Log.console("#{alert_key}:")
          Log.console("  Type: #{violation.type}")
          Log.console("  Current: #{violation.value}")
          Log.console("  Threshold: #{violation.threshold}")
          Log.console("  Count: #{alert_data.count}")
          Log.console("")
        end)
    end
  end

  defp execute_command([command], _opts) do
    Log.error("Unknown command: #{command}")
    print_help()
    System.halt(1)
  end

  defp build_thresholds_from_opts(opts) do
    thresholds = %{}

    thresholds =
      if opts[:render_threshold] do
        Map.put(thresholds, :avg_render_time_ms, opts[:render_threshold])
      else
        thresholds
      end

    thresholds =
      if opts[:memory_threshold] do
        Map.put(thresholds, :memory_usage_mb, opts[:memory_threshold])
      else
        thresholds
      end

    thresholds =
      if opts[:parse_threshold] do
        Map.put(thresholds, :parse_time_us, opts[:parse_threshold])
      else
        thresholds
      end

    if opts[:error_threshold] do
      Map.put(thresholds, :error_rate_percent, opts[:error_threshold])
    else
      thresholds
    end
  end

  defp print_current_metrics(%{timestamp: timestamp} = metrics) do
    time_ago = System.system_time(:millisecond) - timestamp
    Log.console("=== Current Performance Metrics ===")
    Log.console("Last updated: #{time_ago}ms ago")
    Log.console("")

    # Render performance
    render = metrics.render_performance
    Log.console("Render Performance:")
    Log.console("  Average: #{Float.round(render.avg_ms, 2)}ms")
    Log.console("  Maximum: #{Float.round(render.max_ms, 2)}ms")
    Log.console("  Frame count: #{render.count}")
    Log.console("")

    # Parse performance
    parse = metrics.parse_performance
    Log.console("Parse Performance:")
    Log.console("  Average: #{Float.round(parse.avg_us, 1)}μs")
    Log.console("  Maximum: #{Float.round(parse.max_us, 1)}μs")
    Log.console("  Operation count: #{parse.count}")
    Log.console("")

    # Memory usage
    memory = metrics.memory_usage
    Log.console("Memory Usage:")
    Log.console("  Total: #{Float.round(memory.total_mb, 1)}MB")
    Log.console("  Processes: #{Float.round(memory.processes_mb, 1)}MB")
    Log.console("  System: #{Float.round(memory.system_mb, 1)}MB")
    Log.console("")

    # Error rates
    errors = metrics.error_rates
    Log.console("Error Statistics:")
    Log.console("  Error rate: #{Float.round(errors.error_rate_percent, 2)}%")
    Log.console("  Total events: #{errors.total_events}")
    Log.console("  Error events: #{errors.error_events}")
  end

  defp print_current_metrics(_), do: Log.console("No metrics available yet")

  defp print_threshold_status(metrics, thresholds) when is_map(metrics) do
    Log.console("=== Threshold Status ===")

    # Check render thresholds
    render_status =
      if metrics.render_performance.avg_ms > thresholds.avg_render_time_ms do
        "⚠ EXCEEDED"
      else
        "✓ OK"
      end

    Log.console(
      "Render time: #{render_status} (#{Float.round(metrics.render_performance.avg_ms, 2)}ms / #{thresholds.avg_render_time_ms}ms)"
    )

    # Check memory thresholds
    memory_status =
      if metrics.memory_usage.total_mb > thresholds.memory_usage_mb do
        "⚠ EXCEEDED"
      else
        "✓ OK"
      end

    Log.console(
      "Memory usage: #{memory_status} (#{Float.round(metrics.memory_usage.total_mb, 1)}MB / #{thresholds.memory_usage_mb}MB)"
    )

    # Check parse thresholds
    parse_status =
      if metrics.parse_performance.avg_us > thresholds.parse_time_us do
        "⚠ EXCEEDED"
      else
        "✓ OK"
      end

    Log.console(
      "Parse time: #{parse_status} (#{Float.round(metrics.parse_performance.avg_us, 1)}μs / #{thresholds.parse_time_us}μs)"
    )

    # Check error thresholds
    error_status =
      if metrics.error_rates.error_rate_percent > thresholds.error_rate_percent do
        "⚠ EXCEEDED"
      else
        "✓ OK"
      end

    Log.console(
      "Error rate: #{error_status} (#{Float.round(metrics.error_rates.error_rate_percent, 2)}% / #{thresholds.error_rate_percent}%)"
    )
  end

  defp print_threshold_status(_, _),
    do: Log.console("Threshold status: No data available")

  defp print_regression(regression) do
    Log.console("  #{regression.type} (#{regression.metric}):")
    Log.console("    Baseline: #{regression.baseline}")
    Log.console("    Current: #{regression.current}")

    Log.console(
      "    Regression: #{Float.round(regression.regression_percent, 1)}%"
    )

    Log.console("")
  end

  defp generate_performance_report(status, "json") do
    Jason.encode!(status, pretty: true)
  end

  defp generate_performance_report(status, "csv") do
    headers =
      "timestamp,render_avg_ms,render_max_ms,parse_avg_us,memory_total_mb,error_rate_percent\n"

    metrics = status.current_metrics

    row =
      "#{metrics.timestamp}," <>
        "#{metrics.render_performance.avg_ms}," <>
        "#{metrics.render_performance.max_ms}," <>
        "#{metrics.parse_performance.avg_us}," <>
        "#{metrics.memory_usage.total_mb}," <>
        "#{metrics.error_rates.error_rate_percent}\n"

    headers <> row
  end

  defp generate_performance_report(status, "text") do
    """
    === Raxol Performance Report ===
    Generated: #{DateTime.utc_now() |> DateTime.to_string()}
    Monitoring Status: #{if status.monitoring_enabled, do: "Active", else: "Inactive"}
    Active Alerts: #{status.alert_count}

    === Current Metrics ===
    #{format_metrics_for_report(status.current_metrics)}

    === Baseline Comparison ===
    #{format_baseline_comparison(status.baseline_metrics, status.current_metrics)}

    === Threshold Configuration ===
    #{format_thresholds(status.thresholds)}

    === Optimization Recommendations ===
    #{Enum.join(status.recommendations, "\n")}
    """
  end

  defp format_metrics_for_report(metrics) when is_map(metrics) do
    """
    Render Performance:
      Average: #{Float.round(metrics.render_performance.avg_ms, 2)}ms
      Maximum: #{Float.round(metrics.render_performance.max_ms, 2)}ms
      Frame Count: #{metrics.render_performance.count}

    Parse Performance:
      Average: #{Float.round(metrics.parse_performance.avg_us, 1)}μs
      Maximum: #{Float.round(metrics.parse_performance.max_us, 1)}μs
      Operation Count: #{metrics.parse_performance.count}

    Memory Usage:
      Total: #{Float.round(metrics.memory_usage.total_mb, 1)}MB
      Processes: #{Float.round(metrics.memory_usage.processes_mb, 1)}MB
      System: #{Float.round(metrics.memory_usage.system_mb, 1)}MB

    Error Statistics:
      Error Rate: #{Float.round(metrics.error_rates.error_rate_percent, 2)}%
      Total Events: #{metrics.error_rates.total_events}
      Error Events: #{metrics.error_rates.error_events}
    """
  end

  defp format_metrics_for_report(_), do: "No metrics data available"

  defp format_baseline_comparison(baseline, current)
       when is_map(baseline) and is_map(current) do
    render_change =
      (current.render_performance.avg_ms - baseline.render_performance.avg_ms) /
        baseline.render_performance.avg_ms * 100

    memory_change =
      (current.memory_usage.total_mb - baseline.memory_usage.total_mb) /
        baseline.memory_usage.total_mb * 100

    """
    Render Performance Change: #{Float.round(render_change, 1)}%
    Memory Usage Change: #{Float.round(memory_change, 1)}%
    """
  end

  defp format_baseline_comparison(_, _),
    do: "No baseline data available for comparison"

  defp format_thresholds(thresholds) do
    """
    Average Render Time: #{thresholds.avg_render_time_ms}ms
    Maximum Render Time: #{thresholds.max_render_time_ms}ms
    Memory Usage: #{thresholds.memory_usage_mb}MB
    Parse Time: #{thresholds.parse_time_us}μs
    Error Rate: #{thresholds.error_rate_percent}%
    """
  end

  defp print_help do
    Log.console("""
    Raxol Automated Performance Monitoring

    USAGE:
        mix raxol.perf.monitor <COMMAND> [OPTIONS]

    COMMANDS:
        start              Start automated performance monitoring
        stop               Stop automated performance monitoring
        status             Show current monitoring status and metrics
        regression-check   Check for performance regressions
        report             Generate a performance report
        alerts             Show active performance alerts

    OPTIONS:
        --render-threshold FLOAT    Maximum average render time in ms (default: 16.67)
        --memory-threshold FLOAT    Maximum memory usage in MB (default: 50)
        --parse-threshold FLOAT     Maximum parse time in μs (default: 500)
        --error-threshold FLOAT     Maximum error rate percentage (default: 1.0)
        --output FILE              Output file for reports (default: stdout)
        --format FORMAT            Report format: json, csv, or text (default: text)
        --help, -h                 Show this help message

    EXAMPLES:
        # Start monitoring with default thresholds
        mix raxol.perf.monitor start

        # Start with custom render threshold
        mix raxol.perf.monitor start --render-threshold 20

        # Check current status
        mix raxol.perf.monitor status

        # Generate JSON report to file
        mix raxol.perf.monitor report --format json --output perf_report.json

        # Check for performance regressions
        mix raxol.perf.monitor regression-check
    """)
  end

  defp print_recommendations(recommendations) do
    case recommendations != [] do
      true ->
        Log.console("")
        Log.console("=== Optimization Recommendations ===")

        Enum.each(recommendations, fn rec ->
          Log.console("• #{rec}")
        end)

      false ->
        :ok
    end
  end
end
