#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# SPDX-License-Identifier: Apache-2.0
# Copyright Pionix GmbH and Contributors to EVerest
#
"""
author: kai-uwe.hermann@pionix.de
Use edm to create a snapshot of the current directory without polluting the current working dir
"""

import argparse
import yaml
import subprocess
from pathlib import Path
import shutil
import string


def get_tags(path: Path) -> list:
        """Return a list of tags the HEAD points to of the repo at path, or an empty list."""
        tags = []
        try:
            result = subprocess.run(["git", "-C", path, "tag", "--points-at", "HEAD"],
                                    stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=True)
            tags = result.stdout.decode("utf-8").splitlines()
        except subprocess.CalledProcessError:
            return tag

        return tags


def main():
    parser = argparse.ArgumentParser(
        description='create an isolated snapshot with edm')

    parser.add_argument('--working-dir', '-wd', type=str,
                        help='Working directory containing the EVerest workspace (default: .)', default=str(Path.cwd()))
    parser.add_argument('--temp-dir', '-td', type=str,
                        help='Temporary directory for creating the snapshot in (default: working-dir/tmp-for-snapshot)', default=None)
    parser.add_argument('--version', type=str,
                        help='dependency version to override, format is: dependency1:version,dependency2:version2', default=None)
    parser.add_argument('--git-version', action='store_true', help='Use "git" as version when encountering a git hash')
    parser.add_argument('--allow-relative-to-working-dir', action='store_true', help='Allow temporary directory to be relative to working dir (dangerous!)')
    parser.add_argument('--post-process', action='store_true', help='Postprocess existing snapshot')
    parser.add_argument('--include-external-deps', action='store_true', help='Include external dependencies in snapshot')
    parser.add_argument('--exclude-dir', action='append', dest='excluded_dirs', type=str, help='Exclude specified directory from snapshot (can be used multiple times)', default=[])

    args = parser.parse_args()

    working_dir = Path(args.working_dir).expanduser().resolve()

    tmp_dir = working_dir / 'tmp-for-snapshot'

    if args.temp_dir:
        tmp_dir = Path(args.temp_dir).expanduser().resolve()

    if working_dir == tmp_dir:
        print(f'Temporary directory cannot be equal to working directory: {tmp_dir}')
        return 1

    if tmp_dir.is_relative_to(working_dir) and tmp_dir.parent != working_dir and not args.allow_relative_to_working_dir:
        print(f'Temporary directory cannot be relative to working directory: {tmp_dir}')
        return 1

    excluded_paths = []
    for excluded_dir in args.excluded_dirs:
        excluded_path = working_dir / excluded_dir
        excluded_path = excluded_path.expanduser().resolve()
        excluded_paths.append(excluded_path)

    if not args.post_process and tmp_dir.exists():
        print(f'Temporary directory dir already exists, deleting it: {tmp_dir}')
        shutil.rmtree(tmp_dir, ignore_errors=True)
    if not args.post_process:
        tmp_dir.mkdir()

        subdirs = list(working_dir.glob('*/'))
        for subdir in subdirs:
            subdir_path = Path(subdir)
            if not subdir_path.is_dir():
                print(f'{subdir_path} is not a dir, ignoring')
                continue
            if subdir_path == tmp_dir:
                print(f'{subdir_path} is tmp dir, ignoring')
                continue
            if subdir_path in excluded_paths:
                print(f'{subdir_path} is excluded, ignoring')
                continue
            print(f'Copying {subdir_path} to {tmp_dir}')
            destdir = tmp_dir / subdir_path.name

            shutil.copytree(subdir_path, destdir, ignore=shutil.ignore_patterns('build*'))

        print('Running edm snaphot --recursive')
        cmd_line = ['edm']
        if args.include_external_deps:
            cmd_line.append('--external-in-config')
        cmd_line.extend(['snapshot', '--recursive'])
        with subprocess.Popen(cmd_line, stderr=subprocess.PIPE, cwd=tmp_dir) as edm:
            for line in edm.stderr:
                print(line.decode('utf-8'), end='')
    in_snapshot = tmp_dir / 'snapshot.yaml'
    snapshot = None
    with open(in_snapshot, mode='r', encoding='utf-8') as snapshot_file:
        try:
            snapshot = yaml.safe_load(snapshot_file)
        except yaml.YAMLError as e:
            print(f'Error parsing yaml of {in_snapshot}: {e}')
    if snapshot:
        # check if git_tag is a 40 character hex string and assume it is a git_rev
        if args.git_version:
            for dependency, entry in snapshot.items():
                git_tag = ''
                if 'git_tag' in entry:
                    git_tag = entry['git_tag']
                elif 'git_rev' in entry:
                    git_tag = entry['git_rev']
                if len(git_tag) == 40 and all(character in string.hexdigits for character in git_tag):
                    snapshot[dependency]['git_tag'] = 'git'
        if args.version:
            versions = args.version.split(',')
            for dep_versions in versions:
                dependency, version = dep_versions.split(':')
                if dependency in snapshot:
                    print(f'Overriding {dependency} version {snapshot[dependency]["git_tag"]} to {version}')
                    snapshot[dependency]['git_tag'] = version
        for dependency, entry in snapshot.items():
            git_tag = ''
            if 'git_tag' in entry:
                git_tag = entry['git_tag']
            if git_tag == 'latest':
                print(f'{dependency} has tag "latest", check if there is a version tag as well')
                dependency_path = tmp_dir / dependency
                tags = get_tags(dependency_path)
                tags.remove('latest')
                if len(tags) == 1:
                    print('  Fixing "latest" tag in snapshot')
                    snapshot[dependency]['git_tag'] = tags[0]
                else:
                    print(f'  List of tags with "latest" removed: "{tags}" is not directly usable...')

        with open(in_snapshot, mode='w', encoding='utf-8') as snapshot_file:
            yaml.safe_dump(snapshot, snapshot_file, indent=2, sort_keys=False, width=120)
    print('Done')


if __name__ == '__main__':
    main()
