# frozen_string_literal: true

require "fileutils"
require "pathname"
require "pliny/version"
require "uri"

module Pliny::Commands
  class Updater
    attr_accessor :stream

    def self.run(stream = $stdout)
      new(stream).run!
    end

    def initialize(stream = $stdout)
      @stream = stream
    end

    def run!
      unless File.exist?("Gemfile.lock")
        abort("Pliny app not found - looking for Gemfile.lock")
      end

      version_current = get_current_version
      version_target = Gem::Version.new(Pliny::VERSION)

      if version_current == version_target
        display "Version #{version_current} is current, nothing to update."
      elsif version_current > version_target
        display "pliny-update is outdated. Please update it with `gem install pliny` or similar."
      else
        display "Updating from #{version_current} to #{version_target}..."
        ensure_repo_available
        save_patch(version_current, version_target)
        exec_patch
      end
    end

    # we need a local copy of the pliny repo to produce a diff
    def ensure_repo_available
      if File.exist?(repo_dir)
        unless system("cd #{repo_dir} && git fetch --tags")
          abort("Could not update Pliny repo at #{repo_dir}")
        end
      else
        unless system("git clone https://github.com/interagent/pliny.git #{repo_dir}")
          abort("Could not git clone the Pliny repo")
        end
      end
    end

    def get_current_version
      File.read("./Gemfile.lock").split("\n").each do |line|
        next unless (pliny_version = line.match(/pliny \(([\d+.]+)\)/))
        return Gem::Version.new(pliny_version[1])
      end
    end

    def save_patch(curr, target)
      # take a diff of changes that happened to the template app in Pliny
      diff = `cd #{repo_dir} && GIT_CONFIG_GLOBAL=/dev/null git diff v#{curr}..v#{target} lib/template/`

      # remove /lib/template from the path of files in the patch so that we can
      # apply these to the current folder
      diff.gsub!(/(\w)\/lib\/template/, '\1')

      File.open(patch_file, "w") { |f| f.puts diff }
    end

    def exec_patch
      msg = [
        "Pliny update applied. Please review the changes staged for",
        "commit, and consider applying the diff in .rej files manually.",
        "You can then remove these files with `git clean -f`.",
      ].join("\n")
      exec "git apply -v --reject #{patch_file}; echo '\n\n#{msg}'"
    end

    def display(msg)
      stream.puts msg
    end

    def repo_dir
      File.join(Dir.home, ".tmp/pliny-repo")
    end

    def patch_file
      File.join(repo_dir, "pliny-update.patch")
    end
  end
end
