"""Recipe "update-properties": change properties of a document two ways: in one call with an
explicit version, and with check out, set, check in.

Usage: python recipes/update_properties.py [--keep]

With --keep the test document stays in the vault; otherwise it is deleted at the end.
"""

import sys
from datetime import datetime, timezone
from pathlib import Path

sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
from mfiles_client import run  # noqa: E402
from mfiles_values import date, obj_ver, property_value, text  # noqa: E402
from recipe_helpers import (connect, create_test_document, delete, describe,  # noqa: E402
                            format_value, keep)

from mfiles import IRPCObjectOperations_pb2 as ops  # noqa: E402
from mfiles import IRPCObjectOperations_pb2_grpc  # noqa: E402


def main() -> None:
    with connect() as client:
        objects = client.stub(IRPCObjectOperations_pb2_grpc.IRPCObjectOperationsStub)

        created = create_test_document(client, "update-properties")
        object_id = created.object_info.obj_id
        version = created.version_info.version.internal_version
        print(f"Created {describe(created)}")
        try:
            # [START update-properties]
            # One call: the server checks the object out, sets the values and checks it in.
            # Name the version you read; with fail_if_newer_version_exists the call fails instead
            # of overwriting a change someone else made after that version.
            updated = objects.SetProperties(ops.SetPropertiesRequest(
                obj_ver=obj_ver(object_id.type, object_id.item_id.internal_id, version),
                allow_modifying_checked_in_object=True,
                fail_if_newer_version_exists=True,
                # Other properties stay as they are (remove_unspecified_properties is false).
                properties=[property_value(1004, text("grpc, sample"))]))  # Keywords
            version = updated.object_info.latest_version.object_version.version_info \
                .version.internal_version
            print(f"Set Keywords: now version {version}")
            # [END update-properties]

            check_out_set_check_in(objects, object_id)

            # Show the result.
            result = objects.GetObjectInfoAndProperties(ops.GetObjectInfoAndPropertiesRequest(
                obj_id=object_id, latest_version=True))
            for prop in result.property_array:
                if prop.property_def in (1004, 1002):
                    print(f"  Property {prop.property_def}: {format_value(prop.value)}")
        finally:
            if not keep():
                delete(client, object_id)


# [START check-out-in]
def check_out_set_check_in(objects, object_id) -> None:
    """Check out, set, check in: several changes become one new version, and nobody else can
    change the object meanwhile."""
    checked_out = objects.CheckOut(ops.CheckOutRequest(obj_id=object_id))
    working_version = checked_out.object_info.object_version.version_info.version
    print(f"Checked out: working version {working_version.internal_version}")
    working = obj_ver(object_id.type, object_id.item_id.internal_id,
                      working_version.internal_version)
    try:
        objects.SetProperties(ops.SetPropertiesRequest(
            obj_ver=working,
            properties=[
                property_value(1004, text("grpc, sample, checked out")),       # Keywords
                property_value(1002, date(datetime.now(timezone.utc).date())),  # Document date
            ]))
        checked_in = objects.CheckIn(ops.CheckInRequest(obj_ver=working))
    except Exception:
        # Do not leave the object checked out.
        objects.UndoCheckout(ops.UndoCheckoutRequest(obj_ver=working))
        raise
    version = checked_in.object_info.latest_version.object_version.version_info.version
    print(f"Checked in: now version {version.internal_version}")
# [END check-out-in]


if __name__ == "__main__":
    run(main)
