#!/usr/bin/env ruby

require "optparse"
require "open3"

ROOT = File.expand_path("../../", __FILE__)


# Break a repository URL into its separate components
def parse_url(input)
  hosts = "github\.com|bitbucket\.org|gitlab\.com"

  # HTTPS/HTTP link pointing to recognised hosts
  if input =~ /^(?:https?:\/\/)?(?:[^.@]+@)?(?:www\.)?(#{hosts})\/([^\/]+)\/([^\/]+)/i
    { host: $1.downcase(), user: $2, repo: $3.sub(/\.git$/, "") }
  # SSH
  elsif input =~ /^git@(#{hosts}):([^\/]+)\/([^\/]+)\.git$/i
    { host: $1.downcase(), user: $2, repo: $3 }
  # provider:user/repo
  elsif input =~ /^(github|bitbucket|gitlab):\/?([^\/]+)\/([^\/]+)\/?$/i
    { host: $1.downcase(), user: $2, repo: $3 }
  # user/repo - Common GitHub shorthand
  elsif input =~ /^\/?([^\/]+)\/([^\/]+)\/?$/
    { host: "github.com", user: $1, repo: $2 }
  else
    raise "Unsupported URL: #{input}"
  end
end

# Isolate the vendor-name component of a submodule path
def parse_submodule(name)
  name =~ /^(?:.*(?:vendor\/)?grammars\/)?([^\/]+)/i
  path = "vendor/grammars/#{$1}"
  unless File.exist?("#{ROOT}/" + path)
    warn "Submodule '#{path}' does not exist. Aborting."
    exit 1
  end
  path
end

# Print debugging feedback to STDOUT if running with --verbose
def log(msg)
  puts msg if $verbose
end

$aborted = false

def command(*args)
  log "$ #{args.join(' ')}"
  output, status = Open3.capture2e(*args)
  if !status.success?
    output = output.each_line { |line| "  > #{line}" }
    unless $aborted
      $aborted = true
      warn "Command failed. Aborting."
      raise output
    else
      warn output
      exit 1
    end
  end
end

usage = <<~EOH
Usage:
  #{$0} [-v|--verbose] [--replace grammar] url

Examples:
  #{$0} https://github.com/Alhadis/language-roff
  #{$0} --replace sublime-apl https://github.com/Alhadis/language-apl
EOH

$replace = nil
$verbose = true

OptionParser.new do |opts|
  opts.banner = usage
  opts.on("-q", "--quiet", "Do not print output unless there's a failure") do
    $verbose = false
  end
  opts.on("-rSUBMODULE", "--replace=SUBMODDULE", "Replace an existing grammar submodule.") do |name|
    $replace = name
  end
end.parse!


$url = ARGV[0]

# No URL? Print a usage message and bail.
unless $url
  warn usage
  exit 1
end

# Flags to track which changes should be reverted on an error
$gitmodules  = File.read("#{ROOT}/.gitmodules")
$git_config  = File.read("#{ROOT}/.git/config")
$vendor_list = File.read("#{ROOT}/vendor/README.md")

def restore_configs
  File.write("#{ROOT}/.gitmodules", $gitmodules)
  File.write("#{ROOT}/.git/config", $git_config)
end

begin
  # Exit early if Docker isn't installed or running.
  log "Checking Docker is installed and running"
  command('docker', 'ps')

  # Ensure the given URL is an HTTPS link
  parts     = parse_url $url
  $https    = "https://#{parts[:host]}/#{parts[:user]}/#{parts[:repo]}"
  $repo_new = "vendor/grammars/#{parts[:repo]}"
  $repo_old = parse_submodule($replace) if $replace

  Dir.chdir(ROOT)

  if $repo_old
    log "Deregistering: #{$repo_old}"
    command('git', 'submodule', 'deinit', '-f', $repo_old)
    command('git', 'rm', '-rf', $repo_old)
    command('script/grammar-compiler', 'update', '-f')
  end

  log "Registering new submodule: #{$repo_new}"
  command('git', 'submodule', 'add', '-f', $https, $repo_new)
  command('script/grammar-compiler', 'add', $repo_new)

  log "Confirming license"
  if $repo_old
    command('script/licensed')
  else
    repo_abs = File.absolute_path($repo_new)
    command('script/licensed', '--module', repo_abs)
  end

  log "Updating grammar documentation in vendor/README.md"
  command('bundle', 'exec', 'rake', 'samples')
  command('script/sort-submodules')
  command('script/list-grammars')
rescue => ex
  log ex
  if $repo_new
    `git reset HEAD .gitmodules #{$repo_new}`
    `git checkout -- vendor/licenses`
    `rm -rf #{$repo_new}`
    `rm -rf .git/modules/#{$repo_new}/`
    restore_configs()
  end
  if $repo_old
    `rm -rf #{$repo_old}`
    `git submodule add -f "#{$https}", "#{$repo_old}"`
    restore_configs()
  end
  File.write("#{ROOT}/vendor/README.md", $vendor_list)
  `git reset HEAD vendor/licenses`
  `git checkout -- vendor/licenses`
  exit 1
end
