module LearnSubmit
  class Submission
    class GitInteractor
      attr_reader   :username, :git, :message
      attr_accessor :remote_name, :old_remote_name, :old_url

      LEARN_ORG_NAMES = [
        'learn-co-curriculum',
        'learn-co-students'
      ]

      def initialize(username:, message:)
        @username = username
        @message  = message || 'Done.'
        @git      = set_git
      end

      def commit_and_push
        check_remote
        add_changes
        commit_changes

        push!
      end

      def commit
        add_changes
        commit_changes
      end

      def repo_name(remote: remote_name)
        url = git.remote(remote).url
        url.match(/^.+[\w-]+\/(.*?)(?:\.git)?$/)[1]
      end

      def org_name(remote: remote_name)
        url = git.remote(remote).url
        url.match(/^.+github\.com(?::|\/)(.+)\/.*?(?:\.git)?$/)[1]
      end

      def branch_name
        git.branch.name
      end

      private

      def set_git
        begin
          Git.open(FileUtils.pwd)
        rescue ArgumentError => e
          if e.message.match(/path does not exist/)
            puts "It doesn't look like you're in a lesson directory."
            puts 'Please cd into an appropriate directory and try again.'

            exit 1
          else
            puts 'Sorry, something went wrong. Please try again.'
            exit 1
          end
        end
      end

      def check_remote
        if git.remote.url.match(/#{username}/i).nil? && git.remote.url.match(/#{LEARN_ORG_NAMES.join('|').gsub('-','\-')}/i).nil?
          puts "It doesn't look like you're in a lesson directory."
          puts 'Please cd into an appropriate directory and try again.'

          exit 1
        else
          self.remote_name = if git.remote.url.match(/#{username}/i).nil?
            fix_remote!
          else
            git.remote.name
          end
        end
      end

      def fix_remote!
        self.old_remote_name = git.remote.name
        self.old_url         = git.remote.url

        add_backup_remote
        remove_old_remote
        add_correct_remote
      end

      def add_backup_remote
        begin
          git.add_remote("#{old_remote_name}-bak", old_url)
        rescue Git::GitExecuteError => e
          if e.message.match(/already exists/).nil?
            puts "Sorry, something Git-related went wrong. Please try again."
            exit 1
          end
        end
      end

      def remove_old_remote
        git.remote(old_remote_name).remove
      end

      def add_correct_remote
        new_url = old_url.gsub(/#{LEARN_ORG_NAMES.join('|').gsub('-','\-')}/, username)
        git.add_remote(old_remote_name, new_url)

        old_remote_name
      end

      def add_changes
        puts 'Adding changes...'
        git.add(all: true)
      end

      def commit_changes
        puts 'Committing changes...'
        begin
          git.commit(message)
        rescue Git::GitExecuteError => e
          if e.message.match(/nothing to commit/)
            puts "It looks like you have no changes to commit. Will still try updating your submission..."
          else
            puts 'Sorry, something went wrong. Please try again.'
            exit 1
          end
        end
      end

      def push!(retries=3)
        puts 'Pushing changes to GitHub...'
        push_remote = git.remote(self.remote_name)
        begin
          Timeout::timeout(15) do
            git.push(push_remote)
          end
        rescue Timeout::Error
          if retries > 0
            puts "Seems there was an error pushing to GitHub. Trying again..."
            push!(retries-1)
          else
            puts "Can't reach GitHub right now. Please try again."
            exit 1
          end
        end
      end
    end
  end
end
