#!/usr/bin/env python

import argparse, os, tempfile, getpass, yaml
from subprocess import call
from datetime import datetime
from schema import Schema, SchemaError
import git
from pathlib import Path
from sys import platform

config_schema = Schema({
    object: list,
    })

def handle_add(args):
    initial_message = '''New:
    -
Bugfix:
    -
# Uses YAML format. You can use as many categories as you wish, for example:
# New:
#  - Amazing feature added
#  - Another amazing feature added
# Bugfix:
#  - Fixed bug in previous amazing feature
# Other:
#  - Some other entry in changelog
# You can leave also empty categories or add comments. They will be ignored.
# Leave this file unchanged to abort adding entry.
'''
    tf = tempfile.NamedTemporaryFile(mode='w+', suffix=".yaml", encoding='utf-8', delete=False)
    tf_name = tf.name
    tf.write(initial_message)
    tf.flush()
    tf.close()
    print("Running editor:", args.editor)
    call([args.editor, tf_name])
    
    with open(tf_name, mode='r', encoding='utf-8') as f:
        edited_message = f.read()
    if edited_message == initial_message:
        print("Aborted")
        return False
    if not os.path.isdir(args.source_directory):
        os.makedirs(args.source_directory)
    filename = os.path.join(args.source_directory, getpass.getuser() + '_' + datetime.now(tz=None).strftime("%y-%m-%d_%H-%M-%S") + '.yaml')
    print("Creating source file", filename)
    yaml_content = yaml.load(edited_message, Loader=yaml.Loader)
    try:
        config_schema.validate(yaml_content)
    except SchemaError as se:
        raise se
    with open(filename, mode='w+') as f:
        f.write(edited_message)
    g = git.cmd.Git(args.repository_root)
    g.add(args.source_directory)
    g.add(filename)
    
def handle_release(args):
    source_files = [f for f in os.listdir(args.source_directory) if os.path.isfile(os.path.join(args.source_directory, f))]
    if not source_files:
        print("No source files to process changelog.")
        return False
    print("Got source files", source_files)
    new_entries = {}
    for source_file in source_files:
        with open(os.path.join(args.source_directory, source_file), mode='r', encoding='utf-8') as f:
            yaml_content = f.read()
        yml = yaml.load(yaml_content, Loader=yaml.Loader)
        try:
            config_schema.validate(yml)
        except SchemaError as se:
            raise se
        for key, values in yml.items():
            if not values:
                continue
            if key not in new_entries:
                new_entries[key] = []
            new_entries[key] += values

    print("Got new entries", new_entries)
    new_changelog_content = "# " + args.version
    for key, values in new_entries.items():
        header_printed = False
        for value  in values:
            if not value:
                continue
            if not header_printed:
                new_changelog_content += "\n"
                new_changelog_content += "## " + key + '\n'
                header_printed = True
            new_changelog_content += "- " + value + '\n'
    new_changelog_content += "\n"
    changelog_content = ""
    if os.path.isfile(args.changelog_file):
        with open(args.changelog_file, mode='r', encoding='utf-8') as f:
            changelog_content = f.read()
    changelog_content = new_changelog_content + changelog_content
    with open(args.changelog_file, mode='w+', encoding='utf-8') as f:
        f.write(changelog_content)
    print("Added to changelog:\n")
    print(new_changelog_content)
    g = git.cmd.Git(args.repository_root)
    g.add(args.source_directory)
    g.add(args.changelog_file)
    target_archive_directory = os.path.join(args.archive_directory, args.version)
    if not os.path.exists(target_archive_directory):
        os.makedirs(target_archive_directory)
    g.add(target_archive_directory)
    for source_file in source_files:
        full_source_path = Path(args.source_directory) / Path(source_file)
        full_archive_path = Path(target_archive_directory) / Path(source_file)
        print(f"Moving {full_source_path} to {full_archive_path}")
        g.mv(str(full_source_path), str(full_archive_path))


EDITOR = os.environ.get('EDITOR', None)  # that easy!
if EDITOR is None:
    if platform == "win32":
        EDITOR = "notepad"
    else:
        EDITOR = "vim" # I Hate nano :-)
    print("EDITOR not found in environment variables. Using default fallback:", EDITOR)


def main():
    parser = argparse.ArgumentParser(description='Handles changelog entries without conflicts')
    parser.add_argument('--source_directory', type=str, nargs=1, 
                        help='directory where developers entries will be stored. You should keep it in the repository together with your code',
                        default="changelog/sources")
    parser.add_argument('--archive_directory', type=str, nargs=1, 
                        help='directory where developers entries will be moved after releasing final changelog. You should keep it in the repository together with your code',
                        default="changelog/archives")
    parser.add_argument('--changelog_file', type=str, nargs=1, 
                        help='Changelog.md file location. You should keep it in the repository together with your code',
                        default="Changelog.md")
    parser.add_argument('--editor', type=str, nargs=1,
                        help='Text editor used to create entry in changelog', default=EDITOR)
    parser.add_argument('--repository_root', type=str, nargs=1,
                        help='Path to root of git repository', default='.')
    parser.add_argument('action', type=str, nargs=1,  metavar='action', help='add new changelog entry or release previously stored entries. Choices are: add, release', choices=['add', 'release'])
    parser.add_argument('version', type=str, nargs='?', help='version of release to store in changelog. Required if action==release')

    # group = parser.add_mutually_exclusive_group(required=True)

    # subgroup = group.add_argument_group()
    # subgroup.add_argument('release', metavar='release', type=str, nargs=1, help='prepare changelog to release')
    # subgroup.add_argument('version', metavar='version', type=str, nargs=1, help='String with version of release to add to changelog')


    args = parser.parse_args()
    if args.action == ['release'] and not args.version:
        parser.error("version is required for action==release")
    print(args)

    if args.action == ['add']:
        handle_add(args)
    elif args.action == ['release']:
        handle_release(args)
    else:
        raise Exception("Unknown action " + str(args.action))
    
if __name__ == "__main__":
    main()