#!/usr/bin/env python3
# Copyright 2021 The LUCI Authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import argparse
import datetime
import logging
import sys
import hashlib
import os
import contextlib
import json
import subprocess
import re


KNOWN_REPOS = {
    'public': 'https://chromium.googlesource.com/playground/gerrit-cq',
    'internal': 'https://chrome-internal.googlesource.com/playground/cq',
}


def parse_args():
  p = argparse.ArgumentParser()
  p.add_argument('-n', '--number', type=int, required=True,
      help='CLs number')
  p.add_argument('--cq', type=int, required=True,
      help='+1 or +2 to vote on CQ label')
  p.add_argument('-w', '--work-dir', dest='workdir', required=True,
      help='Path where repos are checked out and results are stored')
  p.add_argument('--id',
      help='ID of this test. Defaults to current timestamp')
  p.add_argument('--description', default='UNSPECIFIED',
      help='Description of this test, should fit on one line')
  p.add_argument('--repo', default='internal',
      help='either "internal" or "public"')
  opts = p.parse_args()
  if not opts.id:
    opts.id = datetime.datetime.now().strftime('%Y%m%dT%H%M%S')
  elif ' ' in opts.id:
    p.error('spaces not allowed in --id')
  if opts.repo not in KNOWN_REPOS:
    p.error('only %s are supported for --repo' % sorted(KNOWN_REPOS))
  return opts


def main():
  opts = parse_args()
  logging.basicConfig(level=logging.DEBUG)
  ensure_work_dir(opts)
  checkout_repos(opts)
  cls = create_cls(opts)


def ensure_work_dir(opts):
  if not os.path.exists(opts.workdir):
    logging.info('Creating %r workdir', opts.workdir)
    os.makedirs(opts.workdir)
    return
  if os.path.isdir(opts.workdir):
    return
  raise Exception('%s is not a dir' % opts.workdir)


def checkout_repos(opts):
  for repo, origin in KNOWN_REPOS.items():
    if os.path.exists(os.path.join(opts.workdir, repo)):
      continue
    git('clone', origin, repo, cwd=opts.workdir)


def create_cls(opts):
  cwd = os.path.join(opts.workdir, opts.repo)
  git('freeze', cwd=cwd)  # just in case
  git('fetch', 'origin', cwd=cwd)  # avoid conflicts

  hash_prefix = hashlib.sha1(opts.id.encode('utf8')).hexdigest()[:-4]
  git('new-branch', '%s-%s' % (opts.id, hash_prefix), cwd=cwd)

  parent = os.path.join('cl-stack-tests', opts.id)
  logging.info('Creating %d commits in %s %s', opts.number, opts.repo, parent)
  os.makedirs(os.path.join(cwd, parent))
  for i in range(opts.number):
    gen_commit(opts, n=i+1, rel_parent_dir=parent, hash_prefix=hash_prefix, cwd=cwd)

  rev = git('rev-parse', 'HEAD', cwd=cwd)

  logging.info('Creating %d CLs by pushing %s to Gerrit', opts.number, rev[:8])
  git('push', 'origin', 'HEAD:refs/for/refs/heads/main',
      '-o', 'l=Code-Review+1',
      '-o', 'l=Commit-Queue+2',
      '-o', 'hashtag=cv-test',
      '-o', 'hashtag=%s' % opts.id,
      cwd=cwd)

def gen_commit(opts, n, rel_parent_dir, hash_prefix, cwd):
  rel_fpath = os.path.join(rel_parent_dir, '%03d' % n)
  # Full sha1 hash is 40 chars long.
  suffix_tmpl = '%%0%dd' % (40-len(hash_prefix),)
  change_id = hash_prefix + (suffix_tmpl % (n,))
  with open(os.path.join(cwd, rel_fpath), 'w') as f:
    f.write('Commit #%d\n' % n)
  git('add', rel_fpath, cwd=cwd)
  msg_lines = [
      'Commit #%d' % n,
      '',
      'Test-Description: %s' % opts.description,
      'Test-Id: %s' % opts.id,
      'Change-Id: I%s' % change_id,
  ]
  msg = '\n'.join(msg_lines)
  git('commit', '-m', msg, cwd=cwd)


def git(*args, cwd=None):
  out = subprocess.check_output(['git'] + list(args), cwd=cwd)
  return out.strip()


if __name__ == '__main__':
  main()
  sys.exit(0)

add helper script to create large CL stacks.