#!/usr/bin/env python
import optparse
import os.path
from git import Repo
from git import InvalidGitRepositoryError
import shutil
import logging
from filelock import Timeout, FileLock
from dotenv import dotenv_values, find_dotenv



def clean_up(working_dir):
    if os.path.exists(working_dir):
        logging.info("clean up " + working_dir)
        shutil.rmtree(working_dir)

def get_working_dir(checkout_dir, use_prefix, repository, commit_hash):
    if commit_hash == "HEAD":
        return repository

    working_dir = checkout_dir
    if use_prefix:
        prefix =  os.path.basename(repository)
        working_dir +=  "/"+ prefix + "_" + commit_hash
    else:
        working_dir +=  "/" + commit_hash
    return working_dir


def get_commit_message(commit):
    return "Commit: %s \nAuthor: %s \nDate: %s\n\t%s" % (commit,
            commit.author, commit.committed_datetime, commit.message)

def main():
  logging.getLogger().setLevel(logging.INFO)
  logging.basicConfig(format='[%(levelname)s:checkout_code] %(message)s', level=logging.INFO)

  p = optparse.OptionParser()
  p.add_option('--repository', '-r', help="path to the repository")
  p.add_option('--checkout-dir', '-d',
    help="base path for checkouts")
  p.add_option('--commit-hash', '-c',
    help="the commit hash to be checked out")
  p.add_option('--use-prefix', action="store_true", dest="use_prefix",
    help="prepend the dirname of the repository to the checkout_dir")
  p.add_option('--get-path', action="store_true",
    dest="get_path",
    help="only return the path where the code would be. useful for cli")
  p.add_option('--env', help="path to environment file for configuration")
  p.add_option('--timeout', type=int, help="set timeout (seconds) for file lock.", default=10)


  options, arguments = p.parse_args()

  dotenv_file = options.env if options.env is not None else find_dotenv()
  env_config = {}
  if not os.path.isfile(dotenv_file):
    logging.warning("env file could not be found at %s" % dotenv_file)
  else:
    env_config = dotenv_values(dotenv_file)

  repository = env_config["repository"] if "repository" in env_config else None
  repository = options.repository if options.repository is not None else repository
  if repository is None:
    logging.error("repository required")
    return

  if options.commit_hash is None:
    logging.error("commit-hash required")
    return

  checkout_dir = env_config["checkout_dir"] if "checkout_dir" in env_config else None
  checkout_dir = options.checkout_dir if options.checkout_dir is not None else checkout_dir
  if checkout_dir is None:
    logging.error("checkout_dir required")
    return

  use_prefix = env_config["use_prefix"] == "True" if "use_prefix" in env_config else False
  use_prefix = True if options.use_prefix is not None else use_prefix

  if options.get_path:
      path = get_working_dir(checkout_dir, use_prefix, repository, options.commit_hash)
      if os.path.exists(path):
          print(path)
          return
      else:
          logging.error("Checked out path %s does not exist. Did you run checkout-code without --print-path first?" % path )
          exit(1)

  if options.commit_hash == "HEAD":
      # do nothing for commit HEAD
      return

  if os.path.exists(checkout_dir):
    working_dir = get_working_dir(checkout_dir, use_prefix, repository, options.commit_hash)
    try:
      lock = FileLock(working_dir.rstrip("/")+".lock", timeout=options.timeout)
      with lock:
          if not os.path.exists(working_dir):
            logging.info("checkout repository " + repository + " to " + working_dir)
            os.system("git clone " + repository + " " + working_dir)
          else:
            logging.info("directory " + working_dir + " already exists.")

          try:
            logging.info("check if  " + working_dir + " is git repository.")
            repo = Repo(working_dir)
            assert not repo.bare
            try:
              logging.info("try checking out  " + options.commit_hash)
              repo.head.reference = repo.commit(options.commit_hash)
              message = get_commit_message(repo.head.commit)
              logging.info(message)
              repo.git.reset('--hard')
              logging.info("successfull")

            except ValueError as e:
              logging.info("Commit " + options.commit_hash + " invalid")
              clean_up(working_dir)
          except InvalidGitRepositoryError as e:
              logging.info(working_dir + " is not a valid repository")
    except Timeout:
      logging.error("Could not acquire lock. Timeout.")
  else:
    logging.error("checkout dir " + checkout_dir + " does not exist")
    exit(1)



if __name__ == '__main__':
  main()
