defmodule Raxol.Terminal.ANSI.CachedParser do
  @moduledoc """
  Optimized ANSI parser with caching for common sequences.

  Phase 1 optimization to reduce memory overhead and improve
  performance for frequently used escape sequences.
  """

  @type parsed_token ::
          {:text, binary()}
          | {:csi, binary(), binary()}
          | {:osc, binary()}
          | {:dcs, binary()}
          | {:escape, binary()}

  # Cache for common ANSI sequences
  # These are the most frequently used sequences in terminal applications
  @common_sequences %{
    # SGR (Select Graphic Rendition) - Colors and styles
    # Reset
    "\e[0m" => [{:csi, "0", "m"}],
    # Reset (short form)
    "\e[m" => [{:csi, "", "m"}],
    # Bold
    "\e[1m" => [{:csi, "1", "m"}],
    # Dim
    "\e[2m" => [{:csi, "2", "m"}],
    # Italic
    "\e[3m" => [{:csi, "3", "m"}],
    # Underline
    "\e[4m" => [{:csi, "4", "m"}],
    # Reverse
    "\e[7m" => [{:csi, "7", "m"}],
    # Normal intensity
    "\e[22m" => [{:csi, "22", "m"}],
    # Not italic
    "\e[23m" => [{:csi, "23", "m"}],
    # Not underlined
    "\e[24m" => [{:csi, "24", "m"}],
    # Not reverse
    "\e[27m" => [{:csi, "27", "m"}],

    # Common colors
    # Black foreground
    "\e[30m" => [{:csi, "30", "m"}],
    # Red foreground
    "\e[31m" => [{:csi, "31", "m"}],
    # Green foreground
    "\e[32m" => [{:csi, "32", "m"}],
    # Yellow foreground
    "\e[33m" => [{:csi, "33", "m"}],
    # Blue foreground
    "\e[34m" => [{:csi, "34", "m"}],
    # Magenta foreground
    "\e[35m" => [{:csi, "35", "m"}],
    # Cyan foreground
    "\e[36m" => [{:csi, "36", "m"}],
    # White foreground
    "\e[37m" => [{:csi, "37", "m"}],
    # Default foreground
    "\e[39m" => [{:csi, "39", "m"}],
    # Black background
    "\e[40m" => [{:csi, "40", "m"}],
    # Red background
    "\e[41m" => [{:csi, "41", "m"}],
    # Green background
    "\e[42m" => [{:csi, "42", "m"}],
    # Yellow background
    "\e[43m" => [{:csi, "43", "m"}],
    # Blue background
    "\e[44m" => [{:csi, "44", "m"}],
    # Magenta background
    "\e[45m" => [{:csi, "45", "m"}],
    # Cyan background
    "\e[46m" => [{:csi, "46", "m"}],
    # White background
    "\e[47m" => [{:csi, "47", "m"}],
    # Default background
    "\e[49m" => [{:csi, "49", "m"}],

    # Cursor movement
    # Cursor up
    "\e[A" => [{:csi, "", "A"}],
    # Cursor down
    "\e[B" => [{:csi, "", "B"}],
    # Cursor right
    "\e[C" => [{:csi, "", "C"}],
    # Cursor left
    "\e[D" => [{:csi, "", "D"}],
    # Cursor home
    "\e[H" => [{:csi, "", "H"}],
    # Cursor to 1,1
    "\e[1;1H" => [{:csi, "1;1", "H"}],

    # Erase functions
    # Erase line
    "\e[K" => [{:csi, "", "K"}],
    # Erase to end of line
    "\e[0K" => [{:csi, "0", "K"}],
    # Erase to start of line
    "\e[1K" => [{:csi, "1", "K"}],
    # Erase entire line
    "\e[2K" => [{:csi, "2", "K"}],
    # Erase display
    "\e[J" => [{:csi, "", "J"}],
    # Erase to end of screen
    "\e[0J" => [{:csi, "0", "J"}],
    # Erase to start of screen
    "\e[1J" => [{:csi, "1", "J"}],
    # Erase entire screen
    "\e[2J" => [{:csi, "2", "J"}],

    # Mode changes
    # Show cursor
    "\e[?25h" => [{:csi, "?25", "h"}],
    # Hide cursor
    "\e[?25l" => [{:csi, "?25", "l"}],
    # Use alternate screen
    "\e[?47h" => [{:csi, "?47", "h"}],
    # Use main screen
    "\e[?47l" => [{:csi, "?47", "l"}],
    # Enable alternate screen
    "\e[?1049h" => [{:csi, "?1049", "h"}],
    # Disable alternate screen
    "\e[?1049l" => [{:csi, "?1049", "l"}]
  }

  @doc """
  Parses ANSI escape sequences with caching optimization.

  First checks if the input exactly matches a common cached sequence,
  then falls back to full parsing for complex inputs.
  """
  @spec parse(binary()) :: list(parsed_token())
  def parse(input) when is_binary(input) do
    case Map.get(@common_sequences, input) do
      nil ->
        # Not a simple cached sequence, use full parser
        parse_with_cache_lookup(input)

      cached_result ->
        # Direct cache hit for entire input
        cached_result
    end
  end

  def parse(_), do: []

  # Parse with cache lookup for subsequences
  defp parse_with_cache_lookup(input) do
    case check_for_cached_prefix(input) do
      {cached_result, remaining} ->
        # Found cached prefix, parse remainder
        cached_result ++ parse_with_cache_lookup(remaining)

      nil ->
        # No cached prefix, use original parser
        parse_bytes(input, [], [])
        |> Enum.reverse()
    end
  end

  # Check if input starts with any cached sequence
  defp check_for_cached_prefix(input) do
    @common_sequences
    |> Enum.find_value(fn {cached_seq, result} ->
      if String.starts_with?(input, cached_seq) do
        remaining = String.slice(input, byte_size(cached_seq)..-1//1)
        {result, remaining}
      else
        nil
      end
    end)
  end

  # Original parsing logic for non-cached sequences
  # (Simplified version - delegates to original parser for complex cases)
  defp parse_bytes(input, _text_acc, _acc) do
    # For Phase 1, delegate to original parser
    # This ensures correctness while we optimize the common cases
    Raxol.Terminal.ANSI.Parser.parse(input)
    |> Enum.reverse()
  end

  @doc """
  Benchmark comparison between cached and original parser.
  """
  def benchmark_comparison(input, iterations \\ 10_000) do
    # Warmup
    Enum.each(1..100, fn _ ->
      _ = parse(input)
      _ = Raxol.Terminal.ANSI.Parser.parse(input)
    end)

    # Benchmark cached parser
    {cached_time, _} =
      :timer.tc(fn ->
        Enum.each(1..iterations, fn _ -> parse(input) end)
      end)

    # Benchmark original parser
    {original_time, _} =
      :timer.tc(fn ->
        Enum.each(1..iterations, fn _ ->
          Raxol.Terminal.ANSI.Parser.parse(input)
        end)
      end)

    cached_avg = cached_time / iterations
    original_avg = original_time / iterations
    improvement = (original_avg - cached_avg) / original_avg * 100

    %{
      input: input,
      cached_time_us: cached_avg,
      original_time_us: original_avg,
      improvement_percent: improvement,
      cached_hit: Map.has_key?(@common_sequences, input)
    }
  end

  @doc """
  Get statistics about cache coverage.
  """
  def cache_stats do
    %{
      cached_sequences: map_size(@common_sequences),
      sequence_types: %{
        # Color and style sequences
        sgr_colors: 21,
        # Cursor positioning
        cursor_movement: 6,
        # Screen/line clearing
        erase_functions: 8,
        # Terminal mode switches
        mode_changes: 6
      },
      total_cache_entries: map_size(@common_sequences)
    }
  end
end
