# frozen_string_literal: true

require 'parser/current'
require 'unparser'
require 'tempfile'
require 'rails'
require 'open3'
require 'rspec'
require 'memoist'

require_relative 'zapata/core'
require_relative 'zapata/predictor'
require_relative 'zapata/primitive'
require_relative 'zapata/rzpec/runner'
require_relative 'zapata/rzpec/writer'
require_relative 'zapata/analyst'
require_relative 'zapata/diver'
require_relative 'zapata/db'
require_relative 'zapata/printer'
require_relative 'zapata/version'

module Zapata
  class Revolutionist
    class << self
      attr_accessor :analysis, :analysis_as_array

      def generate_with_friendly_output(filename:, single: false)
        spec_filename = Zapata::Revolutionist.generate(filename: filename, single: single)
        puts "Its done, comrades. File #{spec_filename} was generated."
      end

      def generate(filename:, single: false)
        dirs = single ? [] : %w[app/models]
        file_list = Core::Collector.expand_dirs_to_files(dirs)
        new(file_list).generate_rspec_for(filename, spec_filename(filename))
      end

      def init_analysis_as_array
        @analysis_as_array = analysis.values.flatten
      end

      def spec_filename(filename)
        filename.gsub('app/', 'spec/').gsub('.rb', '_spec.rb')
      end

      private

      def single?(opts, args)
        opts.single? || args.include?('-s') || args.include?('--single')
      end
    end

    def initialize(file_list)
      Core::Loader.load_spec_helper
      self.class.analysis = analyze_multiple(file_list)
    end

    def analyze_multiple(files)
      total = files.size.to_s

      files.each_with_object({}).with_index do |(filename, obj), i|
        puts "[#{adjusted_current(i, total)}/#{total}] Analyzing: #{filename}"
        obj[filename] = Analyst.analyze(filename)
      end
    end

    def adjusted_current(index, total)
      (index + 1).to_s.rjust(total.size)
    end

    def generate_rspec_for(filename, spec_filename)
      unless self.class.analysis[filename]
        self.class.analysis[filename] = Analyst.analyze(filename)
      end

      self.class.init_analysis_as_array

      code = Core::Reader.parse(filename)

      global_analysis = Revolutionist.analysis_as_array
      # first run
      Tempfile.open('spec') do |tempfile|
        RZpec::Writer.new(tempfile.path, code, self.class.analysis[filename], global_analysis)

        save_spec_file(tempfile.path, spec_filename)
        spec_analysis = RZpec::Runner.new(spec_filename)

        # second run with RSpec results
        RZpec::Writer.new(tempfile.path, code, self.class.analysis[filename], global_analysis, spec_analysis)
        save_spec_file(tempfile.path, spec_filename)
      end
    end

    def save_spec_file(tmp_spec_filename, spec_filename)
      spec_path = "#{Dir.pwd}/#{spec_filename}"
      spec_dir = spec_path.split('/')[0...-1].join('/')
      FileUtils.mkdir_p(spec_dir)
      FileUtils.cp(tmp_spec_filename, spec_path)
      spec_filename
    end
  end
end
