#!/usr/bin/env ruby
# frozen_string_literal: true

require 'mutant'
require 'optparse'
require 'unparser'

module Unparser
  module Corpus
    ROOT = Pathname.new(__dir__).parent
    TMP  = ROOT.join('tmp')

    class Project
      include Unparser::Anima.new(:name, :repo_uri, :repo_ref, :exclude)

      # Perform verification via unparser cli
      #
      # @return [Boolean]
      def verify
        checkout
        command = %W[unparser #{repo_path}]
        exclude.each do |name|
          command.push('--ignore', repo_path.join(name).to_s)
        end
        Kernel.system(*command)
      end

    private

      def checkout
        TMP.mkdir unless TMP.directory?

        if repo_path.exist?
          Dir.chdir(repo_path) do
            system(%w[git fetch])
            system(%w[git clean -f -d -x])
          end
        else
          system(%W[git clone #{repo_uri} #{repo_path}])
        end

        Dir.chdir(repo_path) do
          system(%W[git checkout #{repo_ref}])
          system(%w[git reset --hard])
          system(%w[git clean -f -d -x])
        end
      end

      def repo_path
        TMP.join(name)
      end

      def system(arguments)
        return if Kernel.system(*arguments)

        fail "System command #{arguments.inspect} failed!"
      end

      transform    = Mutant::Transform
      string       = transform::Primitive.new(primitive: String)
      string_array = transform::Array.new(transform: string)
      path         = ROOT.join('spec', 'integrations.yml')

      loader =
        transform::Named.new(
          name:      path.to_s,
          transform: transform::Sequence.new(
            steps: [
              transform::Exception.new(
                block:       :read.to_proc,
                error_class: SystemCallError
              ),
              transform::Exception.new(
                block:       YAML.method(:safe_load),
                error_class: YAML::SyntaxError
              ),
              transform::Array.new(
                transform: transform::Sequence.new(
                  steps: [
                    transform::Hash.new(
                      optional: [],
                      required: [
                        transform::Hash::Key.new(value: 'exclude',  transform: string_array),
                        transform::Hash::Key.new(value: 'name',     transform: string),
                        transform::Hash::Key.new(value: 'repo_ref', transform: string),
                        transform::Hash::Key.new(value: 'repo_uri', transform: string)
                      ]
                    ),
                    transform::Hash::Symbolize.new,
                    transform::Exception.new(
                      block:       Project.public_method(:new),
                      error_class: Unparser::Anima::Error
                    )
                  ]
                )
              )
            ]
          )
        )

      ALL = loader.call(path).lmap(&:compact_message).from_right
    end

    # Unparser corpus CLI implementation
    class CLI
      def self.run(*arguments)
        new(*arguments).exit_status
      end

      def initialize(arguments)
        @projects = []

        options = OptionParser.new do |builder|
          builder.on('--list', 'List registered projects') do
            Project::ALL.each do |project|
              puts(project.name)
            end

            Kernel.exit
          end
        end

        options.parse!(arguments).each do |name|
          @projects << project(name)
        end
      end

      def project(name)
        Project::ALL.find { |project| project.name.eql?(name) } || fail("Unregistered project: #{name}")
      end

      def effective_projects
        if @projects.empty?
          Project::ALL
        else
          @projects
        end
      end

      # Return exit status
      #
      # @return [Integer]
      #
      # @api private
      #
      def exit_status
        effective_projects.each do |project|
          project.verify || Kernel.exit(false)
        end

        Kernel.exit
      end

    end # CLI
  end # Corpus
end # Unparser

Unparser::Corpus::CLI.run(ARGV)
