"""Recipe "errors": provoke "not found" and "access denied" and show the decoded M-Files errors.

Usage: python recipes/errors.py

The access-denied case calls a method that needs vault administrator rights; run it as a normal
user to see the error.
"""

import sys
from pathlib import Path

sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
from mfiles_client import MFilesError, run  # noqa: E402
from mfiles_values import obj_id  # noqa: E402
from recipe_helpers import connect  # noqa: E402

from mfiles import IRPCObjectOperations_pb2, IRPCObjectOperations_pb2_grpc  # noqa: E402
from mfiles import IRPCPropertyDefsAdmin_pb2, IRPCPropertyDefsAdmin_pb2_grpc  # noqa: E402

# M-Files error codes this recipe checks for.
E_NOT_FOUND = 0x8004000B
E_ACCESS_DENIED = 0x800407E4


def main() -> None:
    with connect() as client:
        # [START errors]
        # Not found: a document ID that does not exist.
        objects = client.stub(IRPCObjectOperations_pb2_grpc.IRPCObjectOperationsStub)
        try:
            objects.GetObjectInfoAndProperties(
                IRPCObjectOperations_pb2.GetObjectInfoAndPropertiesRequest(
                    obj_id=obj_id(0, 999_999_999), latest_version=True))
            raise RuntimeError("Expected the call to fail.")
        except MFilesError as error:
            if not error.has(E_NOT_FOUND):
                raise
            print_error("Not found", error)

        # Access denied: an administrative call by a user who is not a vault administrator.
        admin = client.stub(IRPCPropertyDefsAdmin_pb2_grpc.IRPCPropertyDefsAdminStub)
        try:
            admin.GetPropertyDefsAdmin(IRPCPropertyDefsAdmin_pb2.GetPropertyDefsAdminRequest())
            print("Access denied: not provoked, the user is a vault administrator")
        except MFilesError as error:
            if not error.has(E_ACCESS_DENIED):
                raise
            print_error("Access denied", error)
        # [END errors]


# [START print-error]
def print_error(label: str, error: MFilesError) -> None:
    print(f"{label}: gRPC status {error.status.name}, "
          f"M-Files error {error.hresult_hex} ({error.hresult_signed})")
    print(f"  Message: {' '.join(error.message.split())}")
    codes = dict.fromkeys(f"0x{code:08X}" for code in error.codes)  # Unique, in order.
    print(f"  Error stack, original error first: {', '.join(codes)}")
# [END print-error]


if __name__ == "__main__":
    run(main)
