#!/usr/share/ucs-test/runner pytest-3 -s -l -vv
## desc: Test the univentionObjectIdentifier handling of the UDM REST API
## tags: [udm,apptest]
## roles: [domaincontroller_master]
## exposure: dangerous
## packages:
##   - univention-directory-manager-rest

import subprocess
import time
import uuid

import pytest
import requests
from requests.auth import HTTPBasicAuth

from univention.testing.ucr import UCSTestConfigRegistry


@pytest.fixture(scope="session")
def email_domain(udm_rest_client, random_name):
    """
    Returns a valid email domain.

    The email domain is valid in the context of the system under test and
    discovered out of the configuration automatically.
    """
    mail_domains_module = udm_rest_client.get("mail/domain")
    mail_domain = mail_domains_module.new()
    mail_domain.properties["name"] = random_name()
    mail_domain.save()
    yield mail_domain.properties["name"]
    mail_domain.delete()


@pytest.fixture
def create_object_identifier_user(udm_rest_client, email_domain, random_name):
    users_user = udm_rest_client.get("users/user")
    test_user = users_user.new()
    username = random_name()
    test_user.properties.update(
        {
            "firstname": random_name(),
            "lastname": random_name(),
            "username": username,
            "displayName": random_name(),
            "password": random_name(),
            "mailPrimaryAddress": f"{username}@{email_domain}",
        },
    )

    def _create(extra_properties: dict | None = None):
        test_user.properties.update(extra_properties or {})
        test_user.save()
        return test_user

    yield _create

    test_user.reload()
    test_user.delete()


@pytest.fixture
def create_object_identifier_group(udm_rest_client, random_name):
    groups_group = udm_rest_client.get("groups/group")
    test_group = groups_group.new()

    test_group.properties.update(
        {
            "name": random_name(),
            "description": random_name(),
        },
    )

    def _create(extra_properties: dict | None = None):
        test_group.properties.update(extra_properties or {})
        test_group.save()
        return test_group

    yield _create

    test_group.reload()
    test_group.delete()


@pytest.fixture
def create_object_identifier_portal_obj(udm_rest_client, random_name):
    portal_module = udm_rest_client.get("portals/portal")
    portal_obj = portal_module.new()
    portal_name = random_name()
    portal_obj.properties.update(
        {
            "name": portal_name,
            "displayName": {
                "en_US": random_name(),
            },
        },
    )

    def _create(extra_properties: dict | None = None):
        portal_obj.properties.update(extra_properties or {})
        portal_obj.save()
        return portal_obj

    yield _create

    portal_obj.reload()
    portal_obj.delete()


class TestUniventionObjectIdentifier:
    @pytest.fixture(scope="class", autouse=True)
    def activate_univentionObjectIdentifier(self):
        with UCSTestConfigRegistry() as ucr:
            ucr.handler_set(['directory/manager/object-identifier/autogeneration=true'])
            subprocess.run(["systemctl", "restart", "univention-directory-manager-rest.service"], check=True)
            # Wait for the UDM REST API to be available
            time.sleep(2)
            yield

    def test_user_autogenerated_univentionObjectIdentifier(self, create_object_identifier_user):
        user = create_object_identifier_user()
        assert user
        user.reload()
        assert user.properties["univentionObjectIdentifier"]

    def test_user_explicitly_set_univentionObjectIdentifier(self, create_object_identifier_user):
        test_identifier = str(uuid.uuid4())
        user = create_object_identifier_user(
            {
                "univentionObjectIdentifier": test_identifier,
            },
        )
        assert user
        user.reload()
        assert user.properties["univentionObjectIdentifier"] == test_identifier

    def test_group_autogenerated_univentionObjectIdentifier(self, create_object_identifier_group):
        group = create_object_identifier_group()
        assert group
        group.reload()
        assert group.properties["univentionObjectIdentifier"]

    def test_group_explicitly_set_univentionObjectIdentifier(self, create_object_identifier_group):
        test_identifier = str(uuid.uuid4())
        group = create_object_identifier_group(
            {
                "univentionObjectIdentifier": test_identifier,
            },
        )
        assert group
        group.reload()
        assert group.properties["univentionObjectIdentifier"] == test_identifier

    def test_portal_obj_autogenerated_univentionObjectIdentifier(self, create_object_identifier_portal_obj):
        """Randomly chosen UDM module to validate that univentionObjectIdentifier is not only configurable on Users and Groups objects"""
        portal_obj = create_object_identifier_portal_obj()
        assert portal_obj
        portal_obj.reload()
        assert portal_obj.properties["univentionObjectIdentifier"]

    def test_portal_obj_explicitly_set_univentionObjectIdentifier(self, create_object_identifier_portal_obj):
        """Randomly chosen UDM module to validate that univentionObjectIdentifier is not only configurable on Users and Groups objects"""
        test_identifier = str(uuid.uuid4())
        portal_obj = create_object_identifier_portal_obj(
            {
                "univentionObjectIdentifier": test_identifier,
            },
        )
        assert portal_obj
        portal_obj.reload()
        assert portal_obj.properties["univentionObjectIdentifier"] == test_identifier

    def test_openapi_schema(self, account, udm_rest_base_url, subtests):
        response = requests.get(
            f"{udm_rest_base_url}openapi.json",
            headers={"Accept": "application/json"},
            auth=HTTPBasicAuth(account.username, account.bindpw),
        )
        response.raise_for_status()
        openapi_schema = response.json()
        assert openapi_schema

        for component, schema in openapi_schema["components"]["schemas"].items():
            props = schema.get("properties", {}).get("properties", {}).get("properties", {})
            with subtests.test(msg=component, i=component):
                if component.endswith("request-patch") and component not in ("users-passwd.request-patch", "blocklists-entry.request-patch", "blocklists-list.request-patch"):
                    assert "univentionObjectIdentifier" in props.keys()
                else:
                    assert "univentionObjectIdentifier" not in props.keys()
