#!/usr/bin/env python

import hashlib
try:
  from urllib2 import urlopen
except ImportError:
  from urllib.request import urlopen
import requests
import json
try:
  from packaging.version import parse
except ImportError:
  from pip._vendor.packaging.version import parse
import re
import tempfile
import shutil
import os
import subprocess

URL_PATTERN = 'https://pypi.python.org/pypi/{package}/json'


def run_commands(*calls):
  """runs the given commands."""
  # get all calls
  for call in calls:
    print(' - ' + ' '.join(call))
    # execute call
    if subprocess.call(call):
      # call failed (has non-zero exit status)
      raise ValueError("Command '%s' failed; stopping" % ' '.join(call))


def get_version(package, url_pattern=URL_PATTERN):
  """Return version of package on pypi.python.org using json."""
  req = requests.get(url_pattern.format(package=package))
  version = parse('0')
  if req.status_code == requests.codes.ok:
    j = json.loads(req.text)
    if 'releases' in j:
      releases = j['releases']
      for release in releases:
        ver = parse(release)
        if not ver.is_prerelease:
          version = max(version, ver)
  return str(version)


def get_remote_md5_sum(url, max_file_size=100 * 1024 * 1024):
  remote = urlopen(url)
  hash = hashlib.md5()

  total_read = 0
  while True:
    data = remote.read(4096)
    total_read += 4096

    if not data or total_read > max_file_size:
      break
    hash.update(data)

  return hash.hexdigest()


class Gitlab(object):
  """A class that wraps Gitlab API using curl"""

  def __init__(self, token):
    super(Gitlab, self).__init__()
    self.token = token
    self.base_url = 'https://gitlab.idiap.ch/api/v3/'
    self.projects_url = self.base_url + 'projects/'

  def get_project(self, project_name, namespace='bob'):
    cmd = ["curl", "--header",
           "PRIVATE-TOKEN: {}".format(self.token),
           self.base_url + "projects/{}%2F{}".format(
               namespace, project_name)]
    pipeline = subprocess.check_output(cmd)
    return json.loads(pipeline.decode())

  def create_pipeline(self, project_id):
    cmd = ["curl", "--request", "POST", "--header",
           "PRIVATE-TOKEN: {}".format(self.token),
           self.base_url + "projects/{}/pipeline?ref=master".format(
               project_id)]
    pipeline = subprocess.check_output(cmd)
    return json.loads(pipeline.decode())

  def get_pipeline(self, project_id, pipeline_id):
    cmd = ["curl", "--header",
           "PRIVATE-TOKEN: {}".format(self.token),
           self.base_url + "projects/{}/pipelines/{}".format(
               project_id, pipeline_id)]
    pipeline = subprocess.check_output(cmd)
    return json.loads(pipeline.decode())

  def create_merge_request(self, project_id, source_branch, target_branch,
                           title, assignee_id='', description='',
                           target_project_id='', labels='', milestone_id='',
                           remove_source_branch=''):
    url = "projects/{}/merge_requests?"
    url += "&".join(['source_branch={}', 'target_branch={}', 'title={}',
                     'assignee_id={}', 'description={}',
                     'target_project_id={}', 'labels={}',
                     'milestone_id={}', 'remove_source_branch={}'])
    url = url.format(project_id, source_branch, target_branch, title,
                     assignee_id, description, target_project_id, labels,
                     milestone_id, remove_source_branch)
    cmd = ["curl", "--request", "POST", "--header",
           "PRIVATE-TOKEN: {}".format(self.token),
           self.base_url + url]
    pipeline = subprocess.check_output(cmd)
    return json.loads(pipeline.decode())

  def accept_merge_request(self, project_id, mergerequest_id,
                           merge_commit_message='',
                           should_remove_source_branch='',
                           merge_when_pipeline_succeeds='', sha=''):
    """
    Update an existing merge request.
    """

    url = "projects/{}/merge_request/{}/merge?"
    url += "&".join([
        'merge_commit_message={}',
        'should_remove_source_branch={}',
        'merge_when_pipeline_succeeds={}',
        'sha={}',
    ])
    url = url.format(project_id, mergerequest_id, merge_commit_message,
                     should_remove_source_branch,
                     merge_when_pipeline_succeeds,
                     sha)
    cmd = ["curl", "--request", "PUT", "--header",
           "PRIVATE-TOKEN: {}".format(self.token),
           self.base_url + url]
    pipeline = subprocess.check_output(cmd)
    try:
      return json.loads(pipeline.decode())
    except Exception:
      return False


def update_meta(meta_path, package):
  stable_version = get_version(package)
  print('latest stable version for {} is {}'.format(package, stable_version))
  url = 'https://pypi.io/packages/source/{0}/{1}/{1}-{2}.zip'.format(
      package[0], package, stable_version)
  md5 = get_remote_md5_sum(url)
  with open(meta_path) as f:
    doc = f.read()
  build_number = '0'
  doc = re.sub(r'\{\s?%\s?set\s?version\s?=\s?".*"\s?%\s?\}',
               '{% set version = "' + str(stable_version) + '" %}',
               doc, count=1)
  doc = re.sub(r'\s+number\:\s?[0-9]+', '\n  number: ' + build_number, doc,
               count=1)
  doc = re.sub(r'\{\s?%\s?set\s?build_number\s?=\s?"[0-9]+"\s?%\s?\}',
               '{% set build_number = "' + build_number + '" %}',
               doc, count=1)
  doc = re.sub(r'\s+md5\:.*', '\n  md5: {}'.format(md5), doc, count=1)
  doc = re.sub(r'\s+url\:.*',
               '\n  url: {}'.format(
                   url.replace(stable_version, '{{ version }}')),
               doc, count=1)
  doc = re.sub(r'\s+home\:.*',
               '\n  home: https://www.idiap.ch/software/bob/',
               doc, count=1)
  doc = doc.replace('Modified BSD License (3-clause)', 'BSD 3-Clause')

  if package == 'bob':
    requrl = 'https://gitlab.idiap.ch/bob/bob/blob/v{}/requirements.txt'
    requrl = requrl.format(stable_version)
    remote = requests.get(requrl)
    req = remote.content.decode()
    req = '\n    - '.join(req.replace('== ', '==').strip().split('\n'))
    be_id = doc.find('bob.extension')
    te_id = doc.find('test:\n', be_id)
    template = '''{req}

  run:
    - python
    - numpy x.x
    - {req}

'''.format(req=req)
    doc = doc[:be_id] + template + doc[te_id:]

  with open(meta_path, 'w') as f:
    f.write(doc)

  return stable_version


def main(package, subfolder='recipes', direct_push=False):
  temp_dir = tempfile.mkdtemp()
  try:
    print("\nClonning bob.conda")
    root = os.path.join(temp_dir, 'bob.conda')
    feedstock = os.path.join(root, subfolder, package)
    try:
      run_commands(
          ['git', 'clone',
           'git@gitlab.idiap.ch:bob/bob.conda.git',
           root])
    except ValueError:
      print("\nFailed to clone `bob.conda`, Exiting ...")
      raise
    os.chdir(feedstock)
    # update meta.yaml
    meta_path = 'meta.yaml'
    stable_version = update_meta(meta_path, package)

    branch_name = '{}-{}'.format(package, stable_version)
    if not direct_push:
      run_commands(
          ['git', 'checkout', '-b', branch_name])

    run_commands(['git', '--no-pager', 'diff'],
                 ['git', 'config', 'user.email',
                  os.environ.get('GITLAB_USER_EMAIL')],
                 ['git', 'config', 'user.name',
                  os.environ.get('GITLAB_USER_ID')],
                 ['git', 'add', '-A'])
    try:
      run_commands(['git', 'commit', '-am',
                    '[{}] Update to version {}'.format(package,
                                                       stable_version)])
    except ValueError:
      print('Feedstock is already uptodate, skipping.')
      return
    if direct_push:
      print(feedstock)
      try:
        answer = raw_input(
            'Would you like to push directly to master?').lower()
      except Exception:
        answer = input('Would you like to push directly to master?').lower()
      if answer.startswith('y') or answer == '':
        run_commands(['git', 'push'])
        print('See the changes at:\n'
              'https://github.com/conda-forge/'
              '{}-feedstock/commits/master\n\n'.format(package))
    else:
      origin_url = 'https://idiapbbb:{}@gitlab.idiap.ch/bob/bob.conda.git'
      origin_url = origin_url.format(os.environ.get('IDIAPBBB_PASS'))
      subprocess.call(['git', 'remote', 'set-url', 'origin', origin_url])
      run_commands(['git', 'push', '--quiet', '--force', '--set-upstream',
                    'origin', branch_name])
      gitlab = Gitlab(os.environ.get('GITLAB_API_TOKEN'))
      project_id = gitlab.get_project('bob.conda')['id']
      title = 'Update-to-{}'.format(branch_name)
      mr = gitlab.create_merge_request(project_id, branch_name, 'master',
                                       title, remove_source_branch='true')
    #   gitlab.accept_merge_request(
        #   project_id, mr['id'], merge_when_pipeline_succeeds='true')
  finally:
    shutil.rmtree(temp_dir)


if __name__ == '__main__':
  import sys
  main(*sys.argv[1:])