import os
import sys
import copy
import stat
import errno
import shutil
import logging
import tempfile
import subprocess
import git
import yaml
from frangidoc.parser import parse_module
from frangidoc.renderer import render


def _handle_remove_read_only(func, path, exc):
  excvalue = exc[1]
  if func in (os.rmdir, os.remove) and excvalue.errno == errno.EACCES:
      os.chmod(path, stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO) # 0777
      func(path)
  else:
      raise


def _save(filepath, content):
    if not os.path.isdir(os.path.dirname(filepath)):
        os.makedirs(os.path.dirname(filepath))
    with open(filepath, "w+") as f_file:
        f_file.write(content)


def generate_and_save(module_filepath, output_filepath=None):
    logging.info('Processing file : {}'.format(module_filepath))

    module = parse_module(module_filepath)
    page = render(module)

    logging.info('Processing done !')

    _save(output_filepath, page)

    logging.info("Output file written : %s" % output_filepath)


def _make_pythonpath_envvar():
    if os.name == 'nt':
        return ';'.join(sys.path)
    return ':'.join(sys.path)


def argparse_and_save(package_name, module_filepath, output_filepath):
    logging.info(" -> Generating Markdown from CLI {}".format(package_name))

    command = "python {} -h".format(module_filepath)
    env = os.environ.copy()
    env['PYTHONPATH'] = _make_pythonpath_envvar()

    try:
        content = subprocess.check_output(command, shell=True, env=env)
    except Exception as e:
        logging.warning("    Error while executing '{}' :".format(command))
        logging.warning(e)
        return

    lines = [
        '# {} : CLI'.format(package_name),
        '',
        'Here is the output generated by running `python -m {} -h` :'.format(package_name),
        '',
        '```text'
    ]

    lines += content.splitlines()
    lines.append('```')

    content = '\n'.join(lines)
    _save(output_filepath, content)

    logging.info("Output file written : %s" % output_filepath)


def clone_and_generate(repository_url, output_directory, cleanup=True):
    # TODO : range moi ca !
    repo_name = os.path.basename(repository_url).replace('.git', '')
    temp_folder = tempfile.mkdtemp(prefix="frangidoc-{}.".format(repo_name))
    config_filepath = os.path.join(temp_folder, '.frangidoc.yml')

    try:
        git.Repo.clone_from(repository_url, temp_folder)
    except git.GitCommandError as e:
        logging.warning("Impossible to clone {repo_url}".format(repo_url=repository_url))
        logging.warning(e)
        return False

    logging.info("Cloned {repo_url} to {temp_folder}".format(repo_url=repository_url, temp_folder=temp_folder))

    if not os.path.isfile(config_filepath):
        logging.warning("Could not find .frangidoc.yml, aborting")
        return False

    with open(config_filepath, 'r') as f_config:
        config = yaml.load(f_config)

    logging.info("Loaded .frangidoc.yml")

    output_folder = os.path.join(output_directory, config['title'])

    if os.path.isdir(output_folder):
        shutil.rmtree(output_folder, ignore_errors=False, onerror=_handle_remove_read_only)

    os.makedirs(output_folder)

    sys_path_backup = copy.deepcopy(sys.path)
    environment_backup = copy.deepcopy(os.environ)

    environment = config.get('environment', dict())
    for key, value in environment.items():
        value = value.replace(':', ';')
        logging.info("Adding to {} : {}".format(key, value))

        os.environ[key] = value

        if key.upper() == 'PYTHONPATH':
            for path in value.split(';'):
                sys.path.append(path)

    sys_path_inter_backup = copy.deepcopy(sys.path)

    for module_filepath in config.get('modules', list()):
        module_fullpath = os.path.join(temp_folder, module_filepath)
        relative_path, filename = os.path.split(module_filepath)
        path = os.path.join(temp_folder, relative_path)
        package_name = os.path.basename(relative_path)
        leaf = '/'.join([step for step in os.path.split(relative_path) if step != ''][1:])
        name = filename.replace('.py', '')

        # todo Deplacer cette logique dans 'generate_and_save()'
        # todo (pour que l'api soit consistante entre 'git' et 'module')
        if name == '__init__':
            name = package_name

        if name in ('__init__', '__main__'):
            path = os.path.dirname(path)

        if name == '__main__':
            output_filename = os.path.join(output_folder, leaf, package_name + ' (CLI).md')
            argparse_and_save(package_name, module_fullpath, output_filename)

        else:
            output_filename = os.path.join(output_folder, leaf, name + '.md')
            generate_and_save(module_fullpath, output_filename)

        sys.path = copy.deepcopy(sys_path_inter_backup)

    sys.path = copy.deepcopy(sys_path_backup)
    os.environ = environment_backup

    for markdown_filepath in config.get('markdown', list()):
        fullpath = os.path.join(temp_folder, markdown_filepath)

        if not os.path.isfile(fullpath):
            logging.info("File not found {}".format(markdown_filepath))

        output = os.path.join(output_folder, markdown_filepath)
        shutil.copy2(fullpath, output)
        logging.info("Copied {}".format(markdown_filepath))

    if cleanup:
        logging.info("Cleaning up {}".format(temp_folder))
        shutil.rmtree(temp_folder, ignore_errors=False, onerror=_handle_remove_read_only)

    return True
