require_relative 'puppet-check/puppet_parser'
require_relative 'puppet-check/ruby_parser'
require_relative 'puppet-check/data_parser'

# interfaces from CLI/tasks and to individual parsers
class PuppetCheck
  # initialize future parser and style check bools
  @future_parser = false
  @style_check = false

  # initialize diagnostic output arrays
  @error_files = []
  @warning_files = []
  @clean_files = []
  @ignored_files = []

  # initialize style arg arrays
  @puppetlint_args = []
  @rubocop_args = []

  # let the parser methods read user options and append to the file arrays; let CLI and tasks write to user options
  class << self
    attr_accessor :future_parser, :style_check, :error_files, :warning_files, :clean_files, :ignored_files, :puppetlint_args, :rubocop_args
  end

  # main runner for PuppetCheck
  def run(paths)
    # grab all of the files to be processed
    files = parse_paths(paths)

    # parse the files
    execute_parsers(files)

    # output the diagnostic results
    self.class.output_results

    # exit code
    self.class.error_files.empty? ? 0 : 2
  end

  # parse the paths and return the array of files
  def parse_paths(paths)
    files = []

    # traverse the unique paths, return all files, and replace // with /
    paths.uniq.each do |path|
      if File.directory?(path)
        files.concat(Dir.glob("#{path}/**/*").select { |subpath| File.file? subpath })
      elsif File.file?(path)
        files.push(path)
      end
    end
    # do not process fixtures
    files.reject! { |file| file =~ /fixtures/ }

    # check that at least one file was found, remove double slashes, and return unique files
    raise "No files found in supplied paths #{paths.join(', ')}." if files.empty?
    files.map! { |file| file.gsub('//', '/') }
    files.uniq
  end

  # categorize and pass the files out to the parsers to determine their status
  def execute_parsers(files)
    PuppetParser.manifest(files.select { |file| File.extname(file) == '.pp' })
    files.reject! { |file| File.extname(file) == '.pp' }
    PuppetParser.template(files.select { |file| File.extname(file) == '.epp' })
    files.reject! { |file| File.extname(file) == '.epp' }
    RubyParser.ruby(files.select { |file| File.extname(file) == '.rb' })
    files.reject! { |file| File.extname(file) == '.rb' }
    RubyParser.template(files.select { |file| File.extname(file) == '.erb' })
    files.reject! { |file| File.extname(file) == '.erb' }
    DataParser.yaml(files.select { |file| file =~ /.*\.ya?ml$/ })
    files.reject! { |file| file =~ /.*\.ya?ml$/ }
    DataParser.json(files.select { |file| File.extname(file) == '.json' })
    files.reject! { |file| File.extname(file) == '.json' }
    RubyParser.librarian(files.select { |file| file =~ /.*(Puppet|Module|Rake|Gem)file$/ })
    files.reject! { |file| file =~ /.*(?:Puppet|Module|Rake|Gem)file$/ }
    files.each { |file| self.class.ignored_files.push("-- #{file}") }
  end

  # output the results for the files that were requested to be checked
  def self.output_results
    puts "\033[31mThe following files have errors:\033[0m", error_files.join("\n\n") unless error_files.empty?
    puts "\n\033[33mThe following files have warnings:\033[0m", warning_files.join("\n\n") unless warning_files.empty?
    puts "\n\033[32mThe following files have no errors or warnings:\033[0m", clean_files unless clean_files.empty?
    puts "\n\033[34mThe following files have unrecognized formats and therefore were not processed:\033[0m", ignored_files unless ignored_files.empty?
  end
end