from __future__ import annotations

import unittest

from govoplan_core.core.modules import (
    CapabilityDocumentation,
    DocumentationConfigurationDecision,
    DocumentationConfigurationProviderRegistration,
    DocumentationCondition,
    DocumentationSourceDefinition,
    DocumentationTopic,
    ModuleManifest,
    user_workflow_scope_condition_issues,
)
from govoplan_core.core.registry import PlatformRegistry, RegistryError


def workflow_topic(
    *,
    conditions: tuple[DocumentationCondition, ...],
    documentation_types: tuple[str, ...] = ("user",),
    kind: str = "workflow",
) -> DocumentationTopic:
    return DocumentationTopic(
        id="example.workflow.task",
        title="Complete a task",
        summary="Complete the example task.",
        documentation_types=documentation_types,  # type: ignore[arg-type]
        conditions=conditions,
        metadata={"kind": kind},
    )


class DocumentationTopicContractTests(unittest.TestCase):
    def test_registry_rejects_empty_documentation_version_range(self) -> None:
        topic = DocumentationTopic(
            id="example.versioned",
            title="Versioned",
            summary="Invalid range",
            version_min="2.0.0",
            version_max_exclusive="2.0.0",
        )

        with self.assertRaisesRegex(RegistryError, "empty version range"):
            registry_for(topic).validate()

    def test_user_workflow_requires_scope_conditions(self) -> None:
        topic = workflow_topic(conditions=())

        self.assertEqual(
            user_workflow_scope_condition_issues(topic),
            ("user workflow topics must declare at least one scope-conditioned alternative",),
        )
        with self.assertRaisesRegex(RegistryError, "scope-conditioned alternative"):
            registry_for(topic).validate()

    def test_every_condition_alternative_must_be_scope_conditioned(self) -> None:
        topic = workflow_topic(
            conditions=(
                DocumentationCondition(required_scopes=("example:item:read",)),
                DocumentationCondition(required_modules=("example",)),
            )
        )

        with self.assertRaisesRegex(RegistryError, r"unscoped alternative\(s\): 2"):
            registry_for(topic).validate()

    def test_scoped_user_workflow_and_non_user_topics_are_accepted(self) -> None:
        scoped = workflow_topic(
            conditions=(
                DocumentationCondition(required_scopes=("example:item:read",)),
                DocumentationCondition(any_scopes=("example:item:write", "example:item:admin")),
            )
        )
        admin_workflow = workflow_topic(
            conditions=(),
            documentation_types=("admin",),
        )
        user_reference = workflow_topic(conditions=(), kind="reference")

        self.assertEqual(user_workflow_scope_condition_issues(scoped), ())
        self.assertEqual(user_workflow_scope_condition_issues(admin_workflow), ())
        self.assertEqual(user_workflow_scope_condition_issues(user_reference), ())
        registry_for(scoped, admin_workflow, user_reference).validate()

    def test_documentation_configuration_and_source_extensions_are_validated(self) -> None:
        resolver = lambda _context, keys: {  # noqa: E731
            key: DocumentationConfigurationDecision(key=key, state="enabled")
            for key in keys
        }
        registry = PlatformRegistry()
        registry.register(ModuleManifest(
            id="example",
            name="Example",
            version="1.0.0",
            documentation_configuration_providers=(
                DocumentationConfigurationProviderRegistration(
                    keys=("example.feature",),
                    resolve=resolver,
                ),
            ),
            documentation_sources=(
                DocumentationSourceDefinition(
                    id="example.handbook",
                    kind="repository",
                    label="Example handbook",
                ),
            ),
        ))

        registry.validate()

        duplicate = PlatformRegistry()
        duplicate.register(ModuleManifest(
            id="example",
            name="Example",
            version="1.0.0",
            documentation_configuration_providers=(
                DocumentationConfigurationProviderRegistration(
                    keys=("example.feature",),
                    resolve=resolver,
                ),
                DocumentationConfigurationProviderRegistration(
                    keys=("example.feature",),
                    resolve=resolver,
                ),
            ),
        ))
        with self.assertRaisesRegex(RegistryError, "duplicate documentation configuration key"):
            duplicate.validate()

    def test_capability_documentation_is_typed_and_must_match_a_provider(self) -> None:
        registry = PlatformRegistry()
        registry.register(ModuleManifest(
            id="example",
            name="Example",
            version="1.0.0",
            capability_factories={"example.lookup": lambda _context: object()},
            capability_documentation={
                "example.lookup": CapabilityDocumentation(
                    label="Example lookup",
                    summary="Resolves example records without exposing provider internals.",
                    contract_version="2",
                    audience=("module_admin",),
                ),
            },
        ))
        registry.validate()

        missing_provider = PlatformRegistry()
        missing_provider.register(ModuleManifest(
            id="example",
            name="Example",
            version="1.0.0",
            capability_documentation={
                "example.lookup": CapabilityDocumentation(
                    label="Example lookup",
                    summary="Resolves example records.",
                ),
            },
        ))
        with self.assertRaisesRegex(RegistryError, "does not provide it"):
            missing_provider.validate()


def registry_for(*topics: DocumentationTopic) -> PlatformRegistry:
    registry = PlatformRegistry()
    registry.register(
        ModuleManifest(
            id="example",
            name="Example",
            version="1.0.0",
            documentation=topics,
        )
    )
    return registry


if __name__ == "__main__":
    unittest.main()
