require 'big_keeper/util/git_operator'
require 'big_keeper/util/gitflow_operator'
require 'big_keeper/model/gitflow_type'
require 'big_keeper/model/operate_type'
require 'big_keeper/util/logger'

module BigKeeper
  # Operator for got
  class GitService
    def start(path, name, type)
      git = GitOperator.new

      branch_name = "#{GitflowType.name(type)}/#{name}"
      if !git.has_remote_branch(path, branch_name) && !git.has_local_branch(path, branch_name)

        verify_special_branch(path, 'master')
        verify_special_branch(path, 'develop')

        GitflowOperator.new.verify_git_flow(path)

        GitflowOperator.new.start(path, name, type)
        git.push_to_remote(path, branch_name)
      else
        verify_checkout(path, branch_name)

        if !git.has_remote_branch(path, branch_name)
          git.push_to_remote(path, branch_name)
        end
      end
    end

    def verify_checkout(path, branch_name)
      Dir.chdir(path) do
        cmd = "git checkout -b #{branch_name}"
        if GitOperator.new.has_branch(path, branch_name)
          cmd = "git checkout #{branch_name}"
        end
        IO.popen(cmd) do |io|
          io.each do |line|
            Logger.error("Checkout #{branch_name} failed.") if line.include? 'error'
          end
        end
      end
    end

    def verify_checkout_pull(path, branch_name)
      GitService.new.verify_checkout(path, branch_name)
      GitService.new.pull(path, branch_name)
    end

    def verify_special_branch(path, name)
      git = GitOperator.new

      if git.has_remote_branch(path, name)
        if git.has_local_branch(path, name)
          if git.has_commits(path, name)
            Logger.error(%Q('#{name}' has unpushed commits, you should fix it manually...))
          end
          pull(path, name)
        else
          git.checkout(path, name)
        end
      else
        verify_checkout(path, name)
        git.push_to_remote(path, name)
      end
    end

    def verify_home_branch(path, branch_name, type)
      Logger.highlight('Sync local branchs from remote, waiting...')
      git = GitOperator.new

      git.fetch(path)

      if OperateType::START == type
        if git.current_branch(path) == branch_name
          Logger.error(%(Current branch is '#{branch_name}' already. Use 'update' please))
        end
        if git.has_branch(path, branch_name)
          Logger.error(%(Branch '#{branch_name}' already exists. Use 'switch' please))
        end
      elsif OperateType::SWITCH == type
        if !git.has_branch(path, branch_name)
          Logger.error(%(Can't find a branch named '#{branch_name}'. Use 'start' please))
        end
        if git.current_branch(path) == branch_name
          Logger.error(%(Current branch is '#{branch_name}' already. Use 'update' please))
        end
      elsif OperateType::UPDATE == type
        if !git.has_branch(path, branch_name)
          Logger.error(%(Can't find a branch named '#{branch_name}'. Use 'start' please))
        end
        if git.current_branch(path) != branch_name
          Logger.error(%(Current branch is not '#{branch_name}'. Use 'switch' please))
        end
      else
        Logger.error(%(Not a valid command for '#{branch_name}'.))
      end
    end

    def current_branch_type(path)
      branch_name = GitOperator.new.current_branch(path)
      if branch_name =~ /^feature\/S*/
        GitflowType::FEATURE
      elsif branch_name =~ /^hotfix\/S*/
        GitflowType::HOTFIX
      elsif branch_name =~ /^release\/S*/
        GitflowType::RELEASE
      else
        GitflowType::FEATURE
      end
    end

    def branchs_with_type(path, type)
      branchs = []
      Dir.chdir(path) do
        IO.popen('git branch -a') do | io |
          io.each do | line |
            branchs << line.gsub('\n', '') if line.include?('develop')
            branchs << line.gsub(/\s/, '') if line =~ /[\s\S]*#{GitflowType.name(type)}*/
          end
        end
      end
      branchs
    end

    def pull(path, branch_name)
      git = GitOperator.new
      current_branch_name = git.current_branch(path)
      if current_branch_name == branch_name
        git.pull(path)
      else
        git.checkout(path, branch_name)
        git.pull(path)
        git.checkout(path, current_branch_name)
      end
    end

    def verify_del(path, branch_name, name, type)
      git = GitOperator.new

      if git.has_local_branch(path, branch_name)
        Logger.highlight("Delete local branch '#{branch_name}' for '#{name}'...")

        if git.current_branch(path) == branch_name
          git.discard(path)
          git.checkout(path, GitflowType.base_branch(type))
        end
        git.del_local(path, branch_name)
      end

      if git.has_remote_branch(path, branch_name)
        Logger.highlight("Delete remote branch '#{branch_name}' for '#{name}'...")
        git.del_remote(path, branch_name)
      end
    end

    def verify_push(path, comment, branch_name, name)
      git = GitOperator.new
      if git.has_changes(path) || git.has_commits(path, branch_name)

        git.commit(path, comment) if git.has_changes(path)

        if git.has_remote_branch(path, branch_name)
          Dir.chdir(path) do
            `git push`
          end
        else
          git.push_to_remote(path, branch_name)
        end

        GitOperator.new.check_push_success(path, branch_name, "origin/#{branch_name}")
      else
        Logger.default("Nothing to push for '#{name}'.")
      end
    end

    def verify_rebase(path, branch_name, name)

      # pull rebased branch
      pull(path, branch_name)

      Dir.chdir(path) do
        IO.popen("git rebase #{branch_name} --ignore-whitespace") do |io|
          unless io.gets
            Logger.error("#{name} is already in a rebase-apply, Please:\n"\
                         "  1.Resolve it;\n"\
                         "  2.Commit the changes;\n"\
                         "  3.Push to remote;\n"\
                         "  4.Create a MR;\n"\
                         "  5.Run 'finish' again.")
          end
          io.each do |line|
            next unless line.include? 'Merge conflict'
            Logger.error("Merge conflict in #{name}, Please:\n"\
                         "  1.Resolve it;\n"\
                         "  2.Commit the changes;\n"\
                         "  3.Push to remote;\n"\
                         "  4.Create a MR;\n"\
                         "  5.Run 'finish' again.")
          end
        end
        if GitOperator.new.current_branch(path) != 'develop' && GitOperator.new.current_branch(path) != 'master'
          `git push -f`
        else
          Logger.error("You should not push 'master' or 'develop'")
        end
      end
    end
  end
end
