"""
Module Contract Test Mixin.

Provides a mixin class for testing that a module correctly implements
the ModuleInterface contract.
"""
from typing import Type

from .interface import (
    ModuleInterface,
    EntityDefinition,
    EndpointDefinition,
    HealthStatus,
    HealthState,
    TaskResult,
)


class ModuleContractTestMixin:
    """
    Test mixin for validating ModuleInterface contract compliance.
    
    Subclass and set `module_class` to your module implementation.
    
    Example:
        class TestPLMModuleContract(ModuleContractTestMixin, TestCase):
            module_class = PLMModule
    """

    module_class: Type[ModuleInterface] = None

    def get_module_instance(self) -> ModuleInterface:
        """Create an instance of the module under test."""
        assert self.module_class is not None, (
            "Set module_class on your test class"
        )
        return self.module_class()

    def test_get_module_info_returns_dict(self):
        """Module info must return a dictionary."""
        module = self.get_module_instance()
        info = module.get_module_info()
        assert isinstance(info, dict), "get_module_info() must return dict"

    def test_get_module_info_has_required_fields(self):
        """Module info must contain name, display_name, version."""
        module = self.get_module_instance()
        info = module.get_module_info()
        for field in ['name', 'display_name', 'version']:
            assert field in info, f"Missing required field: {field}"

    def test_module_name_is_valid(self):
        """Module name must be lowercase with underscores only."""
        module = self.get_module_instance()
        info = module.get_module_info()
        name = info['name']
        assert name == name.lower(), "Module name must be lowercase"
        assert name.replace('_', '').isalnum(), (
            "Module name must be alphanumeric with underscores"
        )

    def test_get_searchable_entities_returns_list(self):
        """Searchable entities must return a list."""
        module = self.get_module_instance()
        entities = module.get_searchable_entities()
        assert isinstance(entities, list)

    def test_searchable_entities_have_valid_structure(self):
        """Each entity definition must have required attributes."""
        module = self.get_module_instance()
        entities = module.get_searchable_entities()
        for entity in entities:
            assert isinstance(entity, EntityDefinition), (
                f"Entity must be EntityDefinition, got {type(entity)}"
            )
            assert entity.name, "Entity must have a name"
            assert entity.model_class, "Entity must have a model_class"

    def test_subscribe_to_events_returns_list_of_strings(self):
        """Event subscriptions must return a list of strings."""
        module = self.get_module_instance()
        events = module.subscribe_to_events()
        assert isinstance(events, list)
        for event in events:
            assert isinstance(event, str), (
                f"Event type must be string, got {type(event)}"
            )

    def test_event_types_use_dot_notation(self):
        """Event types must use dot notation (domain.entity.action)."""
        module = self.get_module_instance()
        events = module.subscribe_to_events()
        for event in events:
            parts = event.split('.')
            assert len(parts) >= 2, (
                f"Event '{event}' must use dot notation "
                f"(e.g., 'domain.entity.action')"
            )

    def test_health_check_returns_health_status(self):
        """Health check must return HealthStatus."""
        module = self.get_module_instance()
        health = module.health_check()
        assert isinstance(health, HealthStatus), (
            f"health_check() must return HealthStatus, got {type(health)}"
        )
        assert isinstance(health.state, HealthState)

    def test_get_api_endpoints_returns_list(self):
        """API endpoints must return a list."""
        module = self.get_module_instance()
        endpoints = module.get_api_endpoints()
        assert isinstance(endpoints, list)

    def test_api_endpoints_have_valid_structure(self):
        """Each endpoint must have required attributes."""
        module = self.get_module_instance()
        endpoints = module.get_api_endpoints()
        valid_methods = {'GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS'}
        for endpoint in endpoints:
            assert isinstance(endpoint, EndpointDefinition), (
                f"Endpoint must be EndpointDefinition, got {type(endpoint)}"
            )
            assert endpoint.path, "Endpoint must have a path"
            assert endpoint.method.upper() in valid_methods, (
                f"Invalid HTTP method: {endpoint.method}"
            )
            assert endpoint.name, "Endpoint must have a name"

    def test_handle_workflow_task_returns_task_result(self):
        """Workflow task handler must return TaskResult."""
        module = self.get_module_instance()
        result = module.handle_workflow_task(
            task_id='test-task-id',
            task_type='test_task',
            context={'test': True},
        )
        assert isinstance(result, TaskResult), (
            f"handle_workflow_task() must return TaskResult, got {type(result)}"
        )

    def test_get_provided_permissions_returns_list(self):
        """Provided permissions must return a list."""
        module = self.get_module_instance()
        perms = module.get_provided_permissions()
        assert isinstance(perms, list)

    def test_get_default_settings_returns_dict(self):
        """Default settings must return a dict."""
        module = self.get_module_instance()
        settings = module.get_default_settings()
        assert isinstance(settings, dict)
