"""Recipe "vault-structure": object types, the classes of documents with their properties, and
IDs resolved by alias.

Usage: python recipes/vault_structure.py [class alias, default "MF.CL.Document"]
[property alias, default "MF.PD.Keywords"]
"""

import sys
from pathlib import Path

sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
from mfiles_client import run  # noqa: E402
from recipe_helpers import connect, string_arg  # noqa: E402

from mfiles import IRPCObjectTypes_pb2, IRPCObjectTypes_pb2_grpc  # noqa: E402
from mfiles import IRPCPropertyDefs_pb2, IRPCPropertyDefs_pb2_grpc  # noqa: E402
from mfiles import IRPCVault_pb2, IRPCVault_pb2_grpc  # noqa: E402
from mfiles import enums_pb2 as enums  # noqa: E402


def main() -> None:
    class_alias = string_arg(0, "MF.CL.Document")
    property_alias = string_arg(1, "MF.PD.Keywords")
    with connect() as client:
        # [START vault-structure]
        # Object types. GetObjectTypes returns the real object types, not the value lists.
        object_types = client.stub(IRPCObjectTypes_pb2_grpc.IRPCObjectTypesStub)
        types = object_types.GetObjectTypes(
            IRPCObjectTypes_pb2.GetObjectTypesRequest()).object_types
        print(f"Object types: {len(types)}")
        for object_type in types:
            print(f"  {object_type.id} {object_type.name_plural}")

        # Property definitions and classes.
        property_defs = client.stub(IRPCPropertyDefs_pb2_grpc.IRPCPropertyDefsStub)
        properties = {p.id: p for p in property_defs.GetPropertyDefs(
            IRPCPropertyDefs_pb2.GetPropertyDefsRequest()).property_defs}
        print(f"Property definitions: {len(properties)}")

        classes = property_defs.GetObjectClassesAndGroups(
            IRPCPropertyDefs_pb2.GetObjectClassesAndGroupsRequest()).classes
        document_classes = [c for c in classes if c.object_type == 0]
        print(f"Document classes: {len(document_classes)}")
        for object_class in document_classes:
            info = object_class.base_info.item_info
            # The properties a new object of the class must have, other than built-in ones.
            required = [properties[p.property_def].name
                        for p in object_class.associated_property_defs
                        if p.is_required and p.property_def in properties
                        and not properties[p.property_def].is_predefined]
            print(f"  {info.obj_id.item_id.internal_id} {info.name}; "
                  f"required: {', '.join(required) or '-'}")
        # [END vault-structure]

        # [START resolve-alias]
        # Code that must work in many vaults refers to classes and properties by alias, not ID.
        # The answer is -1 when no item, or more than one, has the alias.
        vault = client.stub(IRPCVault_pb2_grpc.IRPCVaultStub)
        class_id = vault.GetMetadataStructureItemIdByAlias(
            IRPCVault_pb2.GetMetadataStructureItemIdByAliasRequest(
                item_type=enums.METADATA_STRUCTURE_ITEM_OBJECT_CLASS, alias=class_alias)).id
        property_id = vault.GetMetadataStructureItemIdByAlias(
            IRPCVault_pb2.GetMetadataStructureItemIdByAliasRequest(
                item_type=enums.METADATA_STRUCTURE_ITEM_PROPERTY_DEF, alias=property_alias)).id
        print(f"Class alias {class_alias}: ID {class_id}")
        print(f"Property alias {property_alias}: ID {property_id}")
        # [END resolve-alias]


if __name__ == "__main__":
    run(main)
