#!/usr/bin/env python
# -*- coding: utf8 -*-
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.

from collections import defaultdict
from dataclasses import dataclass, asdict, field
from pathlib import Path
from typing import Set
import glob

import click
import frontmatter
try:
    import ruamel.yaml as yaml
except ImportError:
    import ruamel_yaml as yaml

namespace_comment = "### YamlMime:QSharpNamespace"
warning_comment = """
# This file is automatically generated.
# Please do not modify this file manually, or your changes may be lost when
# documentation is rebuilt.
"""

@dataclass(frozen=True)
class NamespaceItem:
    summary: str = ""
    name: str = ""
    namespace: str = ""
    uid: str = ""
    kind: str = ""

@dataclass
class Namespace:
    summary: str = ""
    uid: str = ""
    name: str = ""
    # NB: We need to set default_factory instead of default, since set is a
    #     mutable type.
    items: Set = field(default_factory=set)

def items_of_kind(items, kind):
    return [
        {
            "uid": item.uid,
            "summary": item.summary
        }
        for item in
        sorted(
            (item for item in items if item.kind == kind),
            key=(lambda item: item.uid)
        )
    ]

@click.command()
@click.argument("sources")
@click.argument("output_path")
def main(sources : str, output_path : str):
    namespaces = defaultdict(Namespace)
    output_path = Path(output_path)
    for source in glob.glob(sources):
        print(source)
        with open(source, 'r', encoding='utf8') as f:
            header, _ = frontmatter.parse(f.read())
        if header['qsharp.kind'] == 'namespace':
            namespaces[header['qsharp.name']].summary = header['qsharp.summary']
            namespaces[header['qsharp.name']].name = header['qsharp.name']
            namespaces[header['qsharp.name']].uid = header['uid']
        else:
            namespaces[header['qsharp.namespace']].items.add(NamespaceItem(
                summary=header['qsharp.summary'],
                name=header['qsharp.name'],
                namespace=header['qsharp.namespace'],
                uid=header["uid"],
                kind=header["qsharp.kind"]
            ))

    for namespace_name, namespace in namespaces.items():
        uid = namespace.uid or namespace_name
        name = namespace.name or namespace_name
        namespace_page = {
            "uid": uid,
            "name": name,
            "summary": namespace.summary,
            "operations": items_of_kind(namespace.items, "operation"),
            "functions": items_of_kind(namespace.items, "function"),
            "newtypes": items_of_kind(namespace.items, "udt")
        }
        
        with open(output_path / f"{name.lower()}.yml", "w", encoding="utf8") as f:
            f.write(namespace_comment + warning_comment + yaml.dump(namespace_page))

    toc_page = [
        {
            "uid": namespace.name,
            "name": namespace_name,
            "items": [
                {
                    "name": item.name,
                    "uid": item.uid
                }
                for item in sorted(
                    namespace.items,
                    key=lambda item: item.uid
                )
            ]
        }
        for namespace_name, namespace in sorted(
            namespaces.items(),
            key=lambda pair: pair[0]
        )
    ]
    with open(output_path / "toc.yml", "w", encoding="utf8") as f:
        f.write(warning_comment + yaml.dump(toc_page))

if __name__ == "__main__":
    main()
