# frozen_string_literal: true

module Nextgen
  class TidyGemfile
    def self.clean!(path = "Gemfile")
      gemfile = new(path)
      gemfile.clean
      gemfile.save
    end

    def initialize(path = "Gemfile")
      @path = path
      @gemfile = File.read(path)
    end

    def include?(gem)
      gemfile.match?(/^\s*gem\s+['"]#{gem}['"]/)
    end

    def clean
      @gemfile = gemfile
        .gsub(/^\s*#.*/, "")          # remove comments
        .gsub(/(\s*\n)+/, "\n")       # remove blank lines
        .sub(/^(ruby.*)/, "\n\\1")    # ensure blank space before "ruby" line
        .sub(/^(gem.*)/, "\n\\1")     # ensure blank space before first "gem" line
        .gsub(/^(group.*)/, "\n\\1")  # ensure blank space before each "group" block
      nil
    end

    def add(gem, version: nil, group: nil, require: nil)
      return false if include?(gem)

      gem_line = build_gem_line(gem, version:, require:, indent: group ? "  " : "")

      if group
        group_line = create_group_if_needed(group)
        gemfile.sub!(/#{Regexp.quote(group_line)}/, '\0' + gem_line)
      else
        gemfile.sub!(/^(#|gem\s)/, gem_line + '\0')
      end

      # Add a blank line after the gem if the subsequent line starts with a comment
      gemfile.sub!(/(#{Regexp.quote(gem_line)})(\s*#)/, "\\1\n\\2")
      true
    end

    def remove(gem)
      !!gemfile.gsub!(/^( *#.*?\n)?\s*gem\s+['"]#{gem}['"].*\n/, "")
    end

    def save
      File.write(@path, gemfile.rstrip + "\n")
      true
    end

    private

    attr_reader :gemfile

    def create_group_if_needed(group)
      group_line = "group " + Array(group).map(&:inspect).join(", ") + " do\n"
      gemfile << "\n#{group_line}end\n" unless gemfile.include?(group_line)
      group_line
    end

    def build_gem_line(gem, version:, require:, indent:)
      line = %(gem "#{gem}")
      line += ", #{version.to_s.inspect}" if version
      line += ", require: #{require.inspect}" unless require.nil?

      indent + line + "\n"
    end
  end
end
