# frozen_string_literal: true

module Gitlab
  module Git
    # This class behaves like a struct with fields :blob_id, :blob_size, :operation, :old_path, :new_path
    # All those fields are (binary) strings or integers
    class RawDiffChange
      attr_reader :blob_id, :blob_size, :old_path, :new_path, :operation

      def initialize(raw_change)
        if raw_change.is_a?(Gitaly::GetRawChangesResponse::RawChange)
          @blob_id = raw_change.blob_id
          @blob_size = raw_change.size
          @old_path = raw_change.old_path_bytes.presence
          @new_path = raw_change.new_path_bytes.presence
          @operation = raw_change.operation&.downcase || :unknown
        else
          parse(raw_change)
        end
      end

      private

      # Input data has the following format:
      #
      # When a file has been modified:
      # 7e3e39ebb9b2bf433b4ad17313770fbe4051649c 669 M\tfiles/ruby/popen.rb
      #
      # When a file has been renamed:
      # 85bc2f9753afd5f4fc5d7c75f74f8d526f26b4f3 107 R060\tfiles/js/commit.js.coffee\tfiles/js/commit.coffee
      def parse(raw_change)
        @blob_id, @blob_size, @raw_operation, raw_paths = raw_change.split(' ', 4)
        @blob_size = @blob_size.to_i
        @operation = extract_operation
        @old_path, @new_path = extract_paths(raw_paths)
      end

      def extract_paths(file_path)
        case operation
        when :copied, :renamed
          file_path.split(/\t/)
        when :deleted
          [file_path, nil]
        when :added
          [nil, file_path]
        else
          [file_path, file_path]
        end
      end

      def extract_operation
        return :unknown unless @raw_operation

        case @raw_operation[0]
        when 'A'
          :added
        when 'C'
          :copied
        when 'D'
          :deleted
        when 'M'
          :modified
        when 'R'
          :renamed
        when 'T'
          :type_changed
        else
          :unknown
        end
      end
    end
  end
end
