# frozen_string_literal: true

module RubyJard
  module Decorators
    ##
    # Manipulate and decorate color for texts.
    # This class translate colors to corresponding escape sequences.
    # Support 24-bit color (#51617d format) or 256 colors (https://jonasjacek.github.io/colors/)
    # Example:
    # - #fafafa => \e[38;2;250;250;250m
    # - #aaa => \e[38;2;170;170;170m
    # - 77 => \e[38;2;170;170;170m
    class ColorDecorator
      HEX_PATTERN_6 = /^#([A-Fa-f0-9]{2})([A-Fa-f0-9]{2})([A-Fa-f0-9]{2})$/i.freeze
      HEX_PATTERN_3 = /^#([A-Fa-f0-9]{1})([A-Fa-f0-9]{1})([A-Fa-f0-9]{1})$/i.freeze
      XTERM_NUMBER_PATTERN = /^([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])$/i.freeze

      CSI_RESET = "\e[0m"
      CSI_FOREGROUND_24BIT = "\e[38;2;%d;%d;%dm"
      CSI_BACKGROUND_24BIT = "\e[48;2;%d;%d;%dm"
      CSI_FOREGROUND_256 = "\e[38;5;%dm"
      CSI_BACKGROUND_256 = "\e[48;5;%dm"

      CSI_ITALIC = "\e[3m"
      CSI_UNDERLINE = "\e[4m"
      CSI_BOLD = "\e[1m"

      STYLES_CSI_MAP = {
        underline: CSI_UNDERLINE,
        italic: CSI_ITALIC,
        bold: CSI_BOLD
      }.freeze

      def initialize(color_scheme)
        @color_scheme = color_scheme
      end

      def decorate(style_names, content)
        attributes = nil
        foreground = nil
        background = nil

        if style_names.is_a?(Symbol)
          styles = @color_scheme.styles_for(style_names)
        else
          attributes = translate_styles(style_names)
          styles = @color_scheme.styles_for(style_names.shift)
        end

        if styles.is_a?(Array)
          foreground = translate_color(styles.shift, true)
          background = translate_color(styles.shift, false)
        elsif !styles.nil?
          raise RubyJard::Error, "Styles for #{style_names.inspect} must be an array"
        end

        "#{foreground}#{background}#{attributes}#{content}#{CSI_RESET}"
      end

      private

      def translate_color(color, foreground)
        if (matches = HEX_PATTERN_6.match(color.to_s))
          red = matches[1].to_i(16)
          green = matches[2].to_i(16)
          blue = matches[3].to_i(16)
          sequence = foreground ? CSI_FOREGROUND_24BIT : CSI_BACKGROUND_24BIT
          format sequence, red, green, blue
        elsif (matches = HEX_PATTERN_3.match(color.to_s))
          red = (matches[1] * 2).to_i(16)
          green = (matches[2] * 2).to_i(16)
          blue = (matches[3] * 2).to_i(16)
          sequence = foreground ? CSI_FOREGROUND_24BIT : CSI_BACKGROUND_24BIT
          format sequence, red, green, blue
        elsif (matches = XTERM_NUMBER_PATTERN.match(color.to_s))
          color = matches[1]
          sequence = foreground ? CSI_FOREGROUND_256 : CSI_BACKGROUND_256
          format sequence, color
        else
          ''
        end
      end

      def translate_styles(styles = [])
        styles.map { |key| STYLES_CSI_MAP[key] }.compact.join
      end
    end
  end
end
