"""
Custom pytest markers for SST-driven testing.

These markers provide metadata linking tests to their SST source,
enabling traceability and documentation generation.

Usage:
    from lightwave.schema.testing import sst_spec, flow_test

    @sst_spec("ROUTE-LOGIN", file="routes.yaml", section="auth")
    def test_login_route():
        pass

    @flow_test(step=1)
    def test_signup_step_1():
        pass
"""

from __future__ import annotations

from functools import wraps
from typing import Any, Callable, TypeVar

import pytest

F = TypeVar("F", bound=Callable[..., Any])


def sst_spec(
    spec_id: str,
    file: str | None = None,
    section: str | None = None,
) -> Callable[[F], F]:
    """
    Mark a test as implementing an SST specification.

    This marker links tests to their source in the YAML SST files,
    enabling:
    - Traceability from test to spec
    - Coverage reports against SST
    - Documentation generation

    Args:
        spec_id: Unique identifier for the specification (e.g., "ROUTE-LOGIN")
        file: SST file this spec comes from (e.g., "routes.yaml")
        section: Section within the file (e.g., "auth")

    Usage:
        @sst_spec("ROUTE-LOGIN", file="routes.yaml", section="auth")
        def test_login_returns_200():
            response = client.get("/login/")
            assert response.status_code == 200

        @sst_spec("FLOW-SIGNUP", file="user_flows.yaml")
        class TestSignupFlow:
            pass
    """

    def decorator(func_or_class: F) -> F:
        # Apply pytest marker
        marker = pytest.mark.sst_spec(spec_id=spec_id, file=file, section=section)

        if isinstance(func_or_class, type):
            # Class decorator
            func_or_class = marker(func_or_class)
            func_or_class._sst_spec = {
                "spec_id": spec_id,
                "file": file,
                "section": section,
            }
        else:
            # Function decorator
            func_or_class = marker(func_or_class)
            func_or_class._sst_spec = {  # type: ignore
                "spec_id": spec_id,
                "file": file,
                "section": section,
            }

        return func_or_class

    return decorator


def flow_test(
    step: int | None = None,
    user_type: str | None = None,
    layout: str | None = None,
) -> Callable[[F], F]:
    """
    Mark a test as part of a user flow.

    This marker provides metadata for flow-based E2E tests,
    enabling step-by-step test organization and reporting.

    Args:
        step: Step number in the flow (1-indexed)
        user_type: Expected user type for this step
        layout: Expected layout for this step

    Usage:
        class TestAuthSignupFlow:
            @flow_test(step=1, user_type="guest", layout="auth")
            def test_signup_page(self, client):
                response = client.get("/signup/")
                assert response.status_code == 200

            @flow_test(step=2, user_type="guest")
            def test_email_confirmation(self, client):
                pass
    """

    def decorator(func: F) -> F:
        marker = pytest.mark.flow_test(step=step, user_type=user_type, layout=layout)
        func = marker(func)
        func._flow_test = {  # type: ignore
            "step": step,
            "user_type": user_type,
            "layout": layout,
        }
        return func

    return decorator


def requires_user_type(user_type: str) -> Callable[[F], F]:
    """
    Mark a test as requiring a specific user type.

    The test will be parametrized with appropriate user fixtures
    based on the SST user type definition.

    Args:
        user_type: User type from rbac.yaml (e.g., "platform_admin")

    Usage:
        @requires_user_type("platform_admin")
        def test_admin_dashboard(authenticated_client):
            response = authenticated_client.get("/lwm-admin/")
            assert response.status_code == 200
    """

    def decorator(func: F) -> F:
        marker = pytest.mark.requires_user_type(user_type=user_type)
        func = marker(func)
        func._requires_user_type = user_type  # type: ignore
        return func

    return decorator


def requires_feature(feature: str) -> Callable[[F], F]:
    """
    Mark a test as requiring a specific feature flag.

    The test will only run for tenants that have the feature enabled.

    Args:
        feature: Feature flag from features.yaml (e.g., "use_teams")

    Usage:
        @requires_feature("use_teams")
        def test_team_creation(tenant_client):
            response = tenant_client.post("/teams/new/")
            assert response.status_code == 200
    """

    def decorator(func: F) -> F:
        marker = pytest.mark.requires_feature(feature=feature)
        func = marker(func)
        func._requires_feature = feature  # type: ignore
        return func

    return decorator


def for_tenant(tenant: str) -> Callable[[F], F]:
    """
    Mark a test as specific to a tenant.

    The test will only run in the context of the specified tenant.

    Args:
        tenant: Tenant slug from domains.yaml (e.g., "cineos")

    Usage:
        @for_tenant("cineos")
        def test_cineos_branding(client):
            response = client.get("/")
            assert "cineOS" in response.content.decode()
    """

    def decorator(func: F) -> F:
        marker = pytest.mark.for_tenant(tenant=tenant)
        func = marker(func)
        func._for_tenant = tenant  # type: ignore
        return func

    return decorator


def validate_against_sst(func: F) -> F:
    """
    Mark a test to validate its assertions against SST.

    This is a meta-marker that enables runtime validation of
    test data against the SST definitions.

    Usage:
        @validate_against_sst
        def test_routes_match_sst(sst_routes, client):
            for route in sst_routes.all():
                response = client.get(route.pattern)
                # Validation happens automatically
    """

    @wraps(func)
    def wrapper(*args: Any, **kwargs: Any) -> Any:
        # Run the test
        result = func(*args, **kwargs)

        # Could add post-test validation here
        # e.g., verify all accessed routes exist in SST

        return result

    wrapper._validate_against_sst = True  # type: ignore
    return wrapper  # type: ignore


# =============================================================================
# Pytest hooks for marker processing
# =============================================================================


def pytest_configure(config: Any) -> None:
    """Register custom markers with pytest."""
    config.addinivalue_line("markers", "sst_spec(spec_id, file, section): Link test to SST specification")
    config.addinivalue_line("markers", "flow_test(step, user_type, layout): Mark test as flow step")
    config.addinivalue_line("markers", "requires_user_type(user_type): Require specific user type")
    config.addinivalue_line("markers", "requires_feature(feature): Require feature flag enabled")
    config.addinivalue_line("markers", "for_tenant(tenant): Run test for specific tenant")
