defmodule Raxol.Terminal.Buffer.Cell do
  @moduledoc """
  Manages terminal cell operations and attributes.
  """

  defstruct [
    :char,
    :foreground,
    :background,
    :attributes,
    :hyperlink,
    :width,
    # Virtual field for backward compatibility
    :fg,
    # Virtual field for backward compatibility
    :bg
  ]

  @type t :: %__MODULE__{
          char: String.t(),
          foreground: integer() | atom() | String.t(),
          background: integer() | atom() | String.t(),
          attributes: map(),
          hyperlink: String.t() | nil,
          width: integer()
        }

  @doc """
  Creates a new cell with default settings.
  """
  def new(opts \\ [])

  def new(opts) when is_list(opts) do
    case opts do
      [] -> %__MODULE__{}
      _ -> new_from_keyword(opts)
    end
  end

  def new(arg) when is_binary(arg) do
    %__MODULE__{char: arg, fg: nil, bg: nil}
  end

  def new(opts) when is_map(opts) do
    new_from_map(opts)
  end

  def new(_), do: %__MODULE__{}

  @doc """
  Creates a new cell with the specified character and style.
  """
  def new(char, style) when is_binary(char) and is_map(style) do
    # Log.info("DEBUG: Cell.new/2 - creating cell with char: #{inspect(char)}, style: #{inspect(style)}")
    foreground = extract_foreground(style)
    background = extract_background(style)

    cell = %__MODULE__{
      char: char,
      foreground: foreground,
      background: background,
      attributes: extract_attributes(style),
      hyperlink: extract_hyperlink(style),
      width: extract_width(style),
      # Virtual field for backward compatibility
      fg: foreground,
      # Virtual field for backward compatibility
      bg: background
    }

    # Log.info("DEBUG: Cell.new/2 - created cell: #{inspect(cell)}")
    cell
  end

  @doc """
  Gets the cell's character.
  """
  def get_char(%__MODULE__{} = cell) do
    cell.char
  end

  @doc """
  Sets the cell's character.
  """
  def set_char(%__MODULE__{} = cell, char) when is_binary(char) do
    %{cell | char: char}
  end

  @doc """
  Gets the cell's foreground color.
  """
  def get_foreground(%__MODULE__{} = cell) do
    cell.foreground
  end

  @doc """
  Sets the cell's foreground color.
  """
  def set_foreground(%__MODULE__{} = cell, color) when is_integer(color) do
    %{cell | foreground: color}
  end

  @doc """
  Gets the cell's background color.
  """
  def get_background(%__MODULE__{} = cell) do
    cell.background
  end

  @doc """
  Sets the cell's background color.
  """
  def set_background(%__MODULE__{} = cell, color) when is_integer(color) do
    %{cell | background: color}
  end

  @doc """
  Gets the cell's attributes.
  """
  def get_attributes(%__MODULE__{} = cell) do
    cell.attributes
  end

  @doc """
  Sets the cell's attributes.
  """
  def set_attributes(%__MODULE__{} = cell, attributes)
      when is_map(attributes) do
    %{cell | attributes: attributes}
  end

  @doc """
  Gets the cell's hyperlink.
  """
  def get_hyperlink(%__MODULE__{} = cell) do
    cell.hyperlink
  end

  @doc """
  Sets the cell's hyperlink.
  """
  def set_hyperlink(%__MODULE__{} = cell, hyperlink)
      when is_binary(hyperlink) or is_nil(hyperlink) do
    %{cell | hyperlink: hyperlink}
  end

  @doc """
  Gets the cell's width.
  """
  def get_width(%__MODULE__{} = cell) do
    cell.width
  end

  @doc """
  Sets the cell's width.
  """
  def set_width(%__MODULE__{} = cell, width)
      when is_integer(width) and width > 0 do
    %{cell | width: width}
  end

  # Backward compatibility functions for old :fg and :bg field names
  @doc """
  Gets the cell's foreground color (backward compatibility).
  """
  def fg(%__MODULE__{} = cell) do
    cell.foreground
  end

  @doc """
  Sets the cell's foreground color (backward compatibility).
  """
  def fg(%__MODULE__{} = cell, color) do
    %{cell | foreground: color}
  end

  @doc """
  Gets the cell's background color (backward compatibility).
  """
  def bg(%__MODULE__{} = cell) do
    cell.background
  end

  @doc """
  Sets the cell's background color (backward compatibility).
  """
  def bg(%__MODULE__{} = cell, color) do
    %{cell | background: color}
  end

  @doc """
  Checks if the cell is empty.
  """
  def empty?(%__MODULE__{} = cell) do
    cell.char == " "
  end

  @doc """
  Resets a cell to its default state.
  """
  def reset(%__MODULE__{} = _cell) do
    %__MODULE__{}
  end

  @doc """
  Copies attributes from one cell to another.
  """
  def copy_attributes(%__MODULE__{} = source, %__MODULE__{} = target) do
    %{
      target
      | foreground: source.foreground,
        background: source.background,
        attributes: source.attributes,
        hyperlink: source.hyperlink
    }
  end

  @doc """
  Validates a cell's data.
  Returns true if the cell is valid, false otherwise.
  """
  def valid?(%__MODULE__{} = cell) do
    valid_char?(cell.char) and
      valid_color?(cell.foreground) and
      valid_color?(cell.background) and
      valid_attributes?(cell.attributes)
  end

  def valid?(_), do: false

  defp valid_char?(char) when is_binary(char) do
    String.length(char) == 1
  end

  defp valid_char?(_), do: false

  defp valid_color?(color) when is_atom(color) do
    color in [
      :default,
      :black,
      :red,
      :green,
      :yellow,
      :blue,
      :magenta,
      :cyan,
      :white
    ]
  end

  defp valid_color?(color) when is_binary(color) do
    String.match?(color, ~r/^#[0-9A-Fa-f]{6}$/)
  end

  defp valid_color?(color) when is_integer(color) do
    color >= 0 and color <= 255
  end

  defp valid_color?(_), do: false

  defp valid_attributes?(attrs) when is_map(attrs) do
    valid_attribute_keys = [
      :bold,
      :italic,
      :underline,
      :strikethrough,
      :blink,
      :reverse,
      :faint,
      :conceal,
      :fraktur,
      :double_underline,
      :framed,
      :encircled,
      :overlined
    ]

    Enum.all?(attrs, fn {key, value} ->
      key in valid_attribute_keys and is_boolean(value)
    end)
  end

  defp valid_attributes?(_), do: false

  defp extract_foreground(style) do
    case Map.get(style, :foreground) do
      nil ->
        # Default white
        7

      fg ->
        fg
    end
  end

  defp extract_background(style) do
    case Map.get(style, :background) do
      nil ->
        # Default black
        0

      bg ->
        bg
    end
  end

  defp extract_attributes(style) do
    case Map.get(style, :attributes) do
      nil ->
        # Extract attributes from TextFormatting struct fields
        %{
          bold: Map.get(style, :bold, false),
          italic: Map.get(style, :italic, false),
          underline: Map.get(style, :underline, false),
          strikethrough: Map.get(style, :strikethrough, false),
          blink: Map.get(style, :blink, false),
          reverse: Map.get(style, :reverse, false),
          faint: Map.get(style, :faint, false),
          conceal: Map.get(style, :conceal, false),
          fraktur: Map.get(style, :fraktur, false),
          double_underline: Map.get(style, :double_underline, false),
          framed: Map.get(style, :framed, false),
          encircled: Map.get(style, :encircled, false),
          overlined: Map.get(style, :overlined, false)
        }

      attrs ->
        attrs
    end
  end

  defp extract_hyperlink(style) do
    case Map.get(style, :hyperlink) do
      nil ->
        case style do
          %{hyperlink: link} -> link
          _ -> nil
        end

      link ->
        link
    end
  end

  defp extract_width(style) do
    case Map.get(style, :width) do
      nil ->
        case style do
          %{width: w} when not is_nil(w) -> w
          _ -> 1
        end

      w ->
        w
    end
  end

  defp new_from_map(opts) do
    foreground = Map.get(opts, :foreground, 7)
    background = Map.get(opts, :background, 0)

    %__MODULE__{
      char: Map.get(opts, :char, " "),
      foreground: foreground,
      background: background,
      attributes: Map.get(opts, :attributes, %{}),
      hyperlink: Map.get(opts, :hyperlink, nil),
      width: Map.get(opts, :width, 1),
      # Virtual field for backward compatibility
      fg: foreground,
      # Virtual field for backward compatibility
      bg: background
    }
  end

  defp new_from_keyword(opts) do
    foreground = Keyword.get(opts, :foreground, 7)
    background = Keyword.get(opts, :background, 0)

    %__MODULE__{
      char: Keyword.get(opts, :char, " "),
      foreground: foreground,
      background: background,
      attributes: Keyword.get(opts, :attributes, %{}),
      hyperlink: Keyword.get(opts, :hyperlink, nil),
      width: Keyword.get(opts, :width, 1),
      # Virtual field for backward compatibility
      fg: foreground,
      # Virtual field for backward compatibility
      bg: background
    }
  end
end
