"""
Runtime Registry - Single Source of Truth Access Layer.

This module provides typed access to all configuration values defined in the
YAML Single Source of Truth. It follows a strict hierarchy:

    1. Database (preferred) - Live runtime state
    2. YAML fallback - Schema definitions

Philosophy:
    NEVER hardcode URLs, endpoints, domains, or configuration values in code.
    Always access them through this registry, which ensures:
    - Type safety via Pydantic models
    - Consistency with SST definitions
    - Automatic DB/YAML fallback
    - Testability (can mock the registry)

Usage in application code:
    from lightwave.schema.core.registry import get_registry

    registry = get_registry()
    login_url = registry.routes.get_url("login")
    webhook_endpoint = registry.webhooks.get_endpoint("stripe")
    tenant_domains = registry.tenants.get_domains("cineos")

Usage in tests:
    from lightwave.schema.core.registry import get_registry

    @pytest.fixture
    def registry():
        return get_registry(use_db=False)  # Force YAML-only for tests

    def test_login_redirects(client, registry):
        response = client.get(registry.routes.get_url("login"))
        assert response.status_code == 200

NOTE: This module uses SST schemas from pydantic.sst as the authoritative
typed representations. LayoutSchema and PageSchemaDefinition are imported
from there rather than being defined locally.
"""

from __future__ import annotations

import logging
from dataclasses import dataclass, field
from functools import lru_cache
from pathlib import Path
from typing import Any

from pydantic import BaseModel, ConfigDict

from lightwave.schema.loaders.base import SchemaLoader
from lightwave.schema.pydantic.sst import LayoutSchema, PageSchemaDefinition

logger = logging.getLogger(__name__)


# =============================================================================
# Pydantic Models for Typed Access
# =============================================================================


class RouteEntry(BaseModel):
    """A single route from routes.yaml or database."""

    model_config = ConfigDict(frozen=True)

    pattern: str
    name: str | None = None
    layout: str | None = None
    requires: str | None = None
    feature_flag: str | None = None
    tenant_aware: bool = True
    verification: str | None = None
    description: str | None = None

    def get_url(self, **kwargs: Any) -> str:
        """Build URL from pattern with substitutions."""
        url = self.pattern
        for key, value in kwargs.items():
            url = url.replace(f"<{key}>", str(value))
            url = url.replace(f"{{{key}}}", str(value))
        return url


class WebhookEntry(BaseModel):
    """A webhook provider from domains.yaml or database."""

    model_config = ConfigDict(frozen=True)

    path: str
    description: str = ""
    events: list[str] = []
    verification: str | None = None

    def get_endpoint(self, event: str | None = None) -> str:
        """Get webhook endpoint URL."""
        base = f"/webhooks/{self.path}/"
        if event:
            return f"{base}{event}/"
        return base


class TenantEntry(BaseModel):
    """A tenant from domains.yaml or database."""

    model_config = ConfigDict(frozen=True)

    slug: str
    display_name: str = ""
    domains: dict[str, str] = {}
    features: list[str] = []

    def get_domain(self, domain_type: str = "primary") -> str | None:
        """Get domain by type (primary, app, api, etc.)."""
        return self.domains.get(domain_type)

    def get_all_domains(self) -> list[str]:
        """Get all domains for this tenant."""
        return list(self.domains.values())

    def has_feature(self, feature: str) -> bool:
        """Check if tenant has a feature enabled."""
        return feature in self.features


# =============================================================================
# Type Aliases - Use SST schemas as canonical types
# =============================================================================
# LayoutEntry and BlueprintEntry are now aliases to the SST Pydantic schemas.
# This ensures a single source of truth for type definitions.

# LayoutEntry is now LayoutSchema from pydantic.sst
LayoutEntry = LayoutSchema

# BlueprintEntry is now PageSchemaDefinition from pydantic.sst
BlueprintEntry = PageSchemaDefinition


class UserTypeEntry(BaseModel):
    """A user type/role from rbac.yaml or database."""

    model_config = ConfigDict(frozen=True)

    name: str
    display_name: str = ""
    description: str = ""
    permissions: list[str] = []
    inherits_from: str | None = None


class UserFlowEntry(BaseModel):
    """A user flow from user_flows.yaml."""

    model_config = ConfigDict(frozen=True)

    name: str
    display_name: str = ""
    requires_user_type: str | None = None
    entry_points: list[str] = []
    steps: list[dict[str, Any]] = []


class EntryPointEntry(BaseModel):
    """An entry point (URL that users can visit)."""

    model_config = ConfigDict(frozen=True)

    url: str
    tenant: str | None = None
    flow: str | None = None

    @property
    def domain(self) -> str:
        """Extract domain from URL."""
        url = self.url
        if "://" in url:
            url = url.split("://", 1)[1]
        return url.split("/")[0].split(":")[0]

    @property
    def path(self) -> str:
        """Extract path from URL."""
        url = self.url
        if "://" in url:
            url = url.split("://", 1)[1]
        parts = url.split("/", 1)
        return "/" + (parts[1] if len(parts) > 1 else "")


# =============================================================================
# Registry Subsystems
# =============================================================================


@dataclass
class RoutesRegistry:
    """Typed access to routes from SST."""

    _routes: dict[str, RouteEntry] = field(default_factory=dict)
    _routes_by_pattern: dict[str, RouteEntry] = field(default_factory=dict)

    def get(self, name: str) -> RouteEntry | None:
        """Get route by name."""
        return self._routes.get(name)

    def get_by_pattern(self, pattern: str) -> RouteEntry | None:
        """Get route by URL pattern."""
        return self._routes_by_pattern.get(pattern)

    def get_url(self, name: str, **kwargs: Any) -> str:
        """Get URL for a named route with substitutions."""
        route = self.get(name)
        if not route:
            raise ValueError(f"Route '{name}' not found in SST")
        return route.get_url(**kwargs)

    def all(self) -> list[RouteEntry]:
        """Get all routes."""
        return list(self._routes.values())

    def by_layout(self, layout: str) -> list[RouteEntry]:
        """Get routes that use a specific layout."""
        return [r for r in self._routes.values() if r.layout == layout]

    def by_auth_level(self, requires: str | None) -> list[RouteEntry]:
        """Get routes that require a specific auth level."""
        return [r for r in self._routes.values() if r.requires == requires]


@dataclass
class WebhooksRegistry:
    """Typed access to webhooks from SST."""

    _webhooks: dict[str, WebhookEntry] = field(default_factory=dict)

    def get(self, provider: str) -> WebhookEntry | None:
        """Get webhook by provider name."""
        return self._webhooks.get(provider)

    def get_endpoint(self, provider: str, event: str | None = None) -> str:
        """Get webhook endpoint URL."""
        webhook = self.get(provider)
        if not webhook:
            raise ValueError(f"Webhook provider '{provider}' not found in SST")
        return webhook.get_endpoint(event)

    def all(self) -> list[WebhookEntry]:
        """Get all webhooks."""
        return list(self._webhooks.values())

    def providers(self) -> list[str]:
        """Get all provider names."""
        return list(self._webhooks.keys())


@dataclass
class TenantsRegistry:
    """Typed access to tenants from SST."""

    _tenants: dict[str, TenantEntry] = field(default_factory=dict)
    _domains_to_tenant: dict[str, str] = field(default_factory=dict)

    def get(self, slug: str) -> TenantEntry | None:
        """Get tenant by slug."""
        return self._tenants.get(slug)

    def get_by_domain(self, domain: str) -> TenantEntry | None:
        """Get tenant by domain."""
        slug = self._domains_to_tenant.get(domain)
        return self._tenants.get(slug) if slug else None

    def get_domains(self, slug: str) -> list[str]:
        """Get all domains for a tenant."""
        tenant = self.get(slug)
        return tenant.get_all_domains() if tenant else []

    def all(self) -> list[TenantEntry]:
        """Get all tenants."""
        return list(self._tenants.values())

    def slugs(self) -> list[str]:
        """Get all tenant slugs."""
        return list(self._tenants.keys())

    def all_domains(self) -> list[str]:
        """Get all domains across all tenants."""
        return list(self._domains_to_tenant.keys())


@dataclass
class LayoutsRegistry:
    """Typed access to layouts from SST."""

    _layouts: dict[str, LayoutEntry] = field(default_factory=dict)

    def get(self, name: str) -> LayoutEntry | None:
        """Get layout by name."""
        return self._layouts.get(name)

    def get_template(self, name: str) -> str:
        """Get template path for a layout."""
        layout = self.get(name)
        if not layout:
            raise ValueError(f"Layout '{name}' not found in SST")
        return layout.template_path

    def all(self) -> list[LayoutEntry]:
        """Get all layouts."""
        return list(self._layouts.values())

    def names(self) -> list[str]:
        """Get all layout names."""
        return list(self._layouts.keys())


@dataclass
class BlueprintsRegistry:
    """Typed access to blueprints from SST."""

    _blueprints: dict[str, BlueprintEntry] = field(default_factory=dict)

    def get(self, name: str) -> BlueprintEntry | None:
        """Get blueprint by name."""
        return self._blueprints.get(name)

    def all(self) -> list[BlueprintEntry]:
        """Get all blueprints."""
        return list(self._blueprints.values())

    def names(self) -> list[str]:
        """Get all blueprint names."""
        return list(self._blueprints.keys())

    def by_layout(self, layout: str) -> list[BlueprintEntry]:
        """Get blueprints that allow a specific layout."""
        return [b for b in self._blueprints.values() if layout in b.allowed_layouts]


@dataclass
class UserTypesRegistry:
    """Typed access to user types from SST."""

    _user_types: dict[str, UserTypeEntry] = field(default_factory=dict)

    def get(self, name: str) -> UserTypeEntry | None:
        """Get user type by name."""
        return self._user_types.get(name)

    def all(self) -> list[UserTypeEntry]:
        """Get all user types."""
        return list(self._user_types.values())

    def names(self) -> list[str]:
        """Get all user type names."""
        return list(self._user_types.keys())

    def is_valid(self, name: str) -> bool:
        """Check if a user type name is valid."""
        return name in self._user_types


@dataclass
class UserFlowsRegistry:
    """Typed access to user flows from SST."""

    _flows: dict[str, UserFlowEntry] = field(default_factory=dict)
    _entry_points: list[EntryPointEntry] = field(default_factory=list)

    def get(self, name: str) -> UserFlowEntry | None:
        """Get user flow by name."""
        return self._flows.get(name)

    def all(self) -> list[UserFlowEntry]:
        """Get all user flows."""
        return list(self._flows.values())

    def names(self) -> list[str]:
        """Get all flow names."""
        return list(self._flows.keys())

    def entry_points(self) -> list[EntryPointEntry]:
        """Get all entry points across all flows."""
        return self._entry_points

    def by_user_type(self, user_type: str) -> list[UserFlowEntry]:
        """Get flows that require a specific user type."""
        return [f for f in self._flows.values() if f.requires_user_type == user_type]


@dataclass
class PackagesRegistry:
    """
    Typed access to package ecosystem from SST.

    Provides typed access to package definitions, CI dependency resolution,
    and version alignment configuration.
    """

    _config: Any = field(default=None, repr=False)

    def get(self, name: str) -> Any:
        """Get package by name."""
        if self._config is None:
            return None
        return self._config.get_package(name)

    def get_ci_mode(self, name: str) -> str:
        """Get CI install method for a package."""
        if self._config is None:
            raise ValueError("Packages registry not loaded")
        return self._config.get_ci_mode(name)

    def all(self) -> list[Any]:
        """Get all packages."""
        if self._config is None:
            return []
        return [self._config.packages[name] for name in self._config.get_package_names()]

    def names(self) -> list[str]:
        """Get all package names."""
        if self._config is None:
            return []
        return self._config.get_package_names()

    def validate_alignment(self) -> list[str]:
        """Validate version alignment across packages."""
        if self._config is None:
            return []
        return self._config.validate_alignment()

    def requires_pat(self, name: str) -> bool:
        """Check if package requires GH_PAT for CI."""
        pkg = self.get(name)
        if pkg is None:
            raise ValueError(f"Package '{name}' not found")
        return pkg.requires_pat()

    def is_publishable(self, name: str) -> tuple[bool, list[str]]:
        """Check if package meets publication criteria."""
        pkg = self.get(name)
        if pkg is None:
            raise ValueError(f"Package '{name}' not found")
        return pkg.is_publishable()


# =============================================================================
# Main Registry
# =============================================================================


@dataclass
class SSTRegistry:
    """
    Single Source of Truth Registry.

    Provides typed access to all configuration values from the SST.
    Prefers database values, falls back to YAML definitions.

    Usage:
        registry = get_registry()
        login_url = registry.routes.get_url("login")
        stripe_endpoint = registry.webhooks.get_endpoint("stripe")
    """

    routes: RoutesRegistry = field(default_factory=RoutesRegistry)
    webhooks: WebhooksRegistry = field(default_factory=WebhooksRegistry)
    tenants: TenantsRegistry = field(default_factory=TenantsRegistry)
    layouts: LayoutsRegistry = field(default_factory=LayoutsRegistry)
    blueprints: BlueprintsRegistry = field(default_factory=BlueprintsRegistry)
    user_types: UserTypesRegistry = field(default_factory=UserTypesRegistry)
    user_flows: UserFlowsRegistry = field(default_factory=UserFlowsRegistry)
    packages: PackagesRegistry = field(default_factory=PackagesRegistry)

    _loaded: bool = field(default=False, repr=False)
    _use_db: bool = field(default=True, repr=False)

    def is_loaded(self) -> bool:
        """Check if registry has been loaded."""
        return self._loaded


def _load_routes_from_yaml(loader: SchemaLoader) -> RoutesRegistry:
    """Load routes from YAML SST."""
    registry = RoutesRegistry()
    data = loader.load("routes")

    for _category, routes_list in data.get("routes", {}).items():
        if not isinstance(routes_list, list):
            continue
        for route_data in routes_list:
            # Copy to avoid modifying original
            entry_data = dict(route_data)
            # Remove fields not in RouteEntry model
            entry_data.pop("view", None)
            entry_data.pop("methods", None)
            entry_data.pop("requires_team_membership", None)
            entry = RouteEntry(**entry_data)
            if entry.name:
                registry._routes[entry.name] = entry
            registry._routes_by_pattern[entry.pattern] = entry

    return registry


def _load_webhooks_from_yaml(loader: SchemaLoader) -> WebhooksRegistry:
    """Load webhooks from YAML SST."""
    registry = WebhooksRegistry()
    data = loader.load("domains")

    for provider, webhook_data in data.get("webhooks", {}).items():
        if isinstance(webhook_data, dict):
            webhook_data["path"] = webhook_data.get("path", provider)
            entry = WebhookEntry(**webhook_data)
            registry._webhooks[provider] = entry

    return registry


def _load_tenants_from_yaml(loader: SchemaLoader) -> TenantsRegistry:
    """Load tenants from YAML SST."""
    registry = TenantsRegistry()
    data = loader.load("domains")

    for slug, tenant_data in data.get("tenants", {}).items():
        if isinstance(tenant_data, dict):
            domains = tenant_data.get("domains", {})
            features = tenant_data.get("features", [])
            if isinstance(features, dict):
                features = [k for k, v in features.items() if v]

            entry = TenantEntry(
                slug=slug,
                display_name=tenant_data.get("display_name", slug),
                domains=domains if isinstance(domains, dict) else {},
                features=features,
            )
            registry._tenants[slug] = entry

            # Build domain -> tenant lookup
            for domain in entry.get_all_domains():
                registry._domains_to_tenant[domain] = slug

    return registry


def _load_layouts_from_yaml(loader: SchemaLoader) -> LayoutsRegistry:
    """
    Load layouts from YAML SST using LayoutSchema.

    Uses LayoutSchema.get_all_from_sst() to ensure all validation
    is handled by the canonical SST schema.
    """
    registry = LayoutsRegistry()

    try:
        # Use SST schema's own loading method for proper validation
        layouts = LayoutSchema.get_all_from_sst()
        for layout in layouts:
            registry._layouts[layout.name] = layout
    except FileNotFoundError:
        # Fallback to loader if SST file not found
        logger.warning("LayoutSchema SST file not found, using loader fallback")
        data = loader.load("layouts")
        for name, layout_data in data.get("layouts", {}).items():
            if isinstance(layout_data, dict):
                entry_data = {"name": name, **layout_data}
                entry = LayoutSchema(**entry_data)
                registry._layouts[name] = entry

    return registry


def _load_blueprints_from_yaml(loader: SchemaLoader) -> BlueprintsRegistry:
    """
    Load blueprints/page schemas from YAML SST using PageSchemaDefinition.

    Uses PageSchemaDefinition.get_all_from_sst() to ensure all validation
    is handled by the canonical SST schema.
    """
    registry = BlueprintsRegistry()

    try:
        # Use SST schema's own loading method for proper validation
        schemas = PageSchemaDefinition.get_all_from_sst()
        for schema in schemas:
            registry._blueprints[schema.name] = schema
    except FileNotFoundError:
        # Fallback to loader if SST file not found
        logger.warning("PageSchemaDefinition SST file not found, using loader fallback")
        data = loader.load("blueprints")
        if "blueprints" not in data:
            raise KeyError("blueprints.yaml missing required 'blueprints' key")
        for name, bp_data in data["blueprints"].items():
            if isinstance(bp_data, dict):
                entry_data = {"name": name, **bp_data}
                entry = PageSchemaDefinition(**entry_data)
                registry._blueprints[name] = entry

    return registry


def _load_user_types_from_yaml(loader: SchemaLoader) -> UserTypesRegistry:
    """Load user types from YAML SST."""
    registry = UserTypesRegistry()
    data = loader.load("rbac")

    # user_roles can be either simple name: name pairs or nested objects
    user_roles = data.get("user_roles", {})
    if not user_roles:
        user_roles = data.get("user_types", {})

    for name, role_data in user_roles.items():
        if isinstance(role_data, dict):
            entry = UserTypeEntry(
                name=name,
                display_name=role_data.get("display_name", name),
                description=role_data.get("description", ""),
                permissions=role_data.get("permissions", []),
                inherits_from=role_data.get("inherits_from"),
            )
        else:
            # Simple name: name format (e.g., platform_admin: platform_admin)
            entry = UserTypeEntry(
                name=name,
                display_name=name.replace("_", " ").title(),
                description="",
                permissions=[],
                inherits_from=None,
            )
        registry._user_types[name] = entry

    return registry


def _load_user_flows_from_yaml(loader: SchemaLoader) -> UserFlowsRegistry:
    """Load user flows from YAML SST."""
    registry = UserFlowsRegistry()
    data = loader.load("user_flows")

    all_entry_points: list[EntryPointEntry] = []

    for name, flow_data in data.get("user_flows", {}).items():
        if isinstance(flow_data, dict):
            entry_points_raw = flow_data.get("entry_points", [])
            entry_points_str = []

            for ep in entry_points_raw:
                if isinstance(ep, str):
                    entry_points_str.append(ep)
                    all_entry_points.append(EntryPointEntry(url=ep, flow=name))
                elif isinstance(ep, dict) and "url" in ep:
                    entry_points_str.append(ep["url"])
                    all_entry_points.append(EntryPointEntry(url=ep["url"], tenant=ep.get("tenant"), flow=name))

            entry = UserFlowEntry(
                name=name,
                display_name=flow_data.get("name", name),
                requires_user_type=flow_data.get("requires_user_type"),
                entry_points=entry_points_str,
                steps=flow_data.get("steps", []),
            )
            registry._flows[name] = entry

    registry._entry_points = all_entry_points
    return registry


def _load_packages_from_yaml(loader: SchemaLoader) -> PackagesRegistry:
    """
    Load packages from YAML SST.

    Uses PackageEcosystemSchema to load and validate the entire packages.yaml
    configuration.
    """
    registry = PackagesRegistry()

    try:
        from lightwave.schema.pydantic.models.packages import PackageEcosystemSchema

        config = PackageEcosystemSchema.load()
        registry._config = config
    except FileNotFoundError:
        logger.warning("packages.yaml not found, packages registry will be empty")
    except Exception as e:
        logger.warning(f"Failed to load packages.yaml: {e}")

    return registry


def _load_from_database(registry: SSTRegistry) -> SSTRegistry:
    """
    Load/override registry values from database.

    This is called when use_db=True to get live runtime values.
    Falls back gracefully if models don't exist or DB is unavailable.

    Note: DB records may have fewer fields than SST schemas (which include
    additional metadata like use_cases, context_requirements). We use defaults
    for those optional fields.
    """
    try:
        from django.apps import apps

        # Try to load layouts from DB (sites.Layout in v2.0, cms.Layout legacy)
        try:
            try:
                Layout = apps.get_model("sites", "Layout")
            except LookupError:
                Layout = apps.get_model("cms", "Layout")

            for layout in Layout.objects.filter(is_active=True):
                # LayoutSchema requires all fields - provide defaults for optional ones
                entry = LayoutSchema(
                    name=layout.name,
                    template_path=layout.template_path,
                    description=getattr(layout, "description", ""),
                    auth_level=getattr(layout, "auth_level", None),
                    is_active=layout.is_active,
                    # These fields are SST-only metadata, use defaults
                    use_cases=[],
                    context_requirements=[],
                )
                registry.layouts._layouts[layout.name] = entry
        except (LookupError, Exception):
            pass  # Model doesn't exist or DB error, keep YAML values

        # Try to load blueprints/page schemas from DB (sites.PageSchema in v2.0)
        try:
            try:
                PageSchema = apps.get_model("sites", "PageSchema")
            except LookupError:
                PageSchema = apps.get_model("cms", "Blueprint")

            for ps in PageSchema.objects.filter(is_active=True):
                # PageSchemaDefinition includes validation fields
                entry = PageSchemaDefinition(
                    name=ps.name,
                    display_name=getattr(ps, "display_name", ps.name),
                    description=getattr(ps, "description", ""),
                    is_locked=getattr(ps, "is_locked", False),
                    is_active=ps.is_active,
                    allowed_layouts=getattr(ps, "allowed_layouts", []),
                    default_layout=getattr(ps, "default_layout", "web"),
                    default_islands=[],  # SST-only metadata
                    schema=getattr(ps, "schema", {}),
                )
                registry.blueprints._blueprints[ps.name] = entry
        except (LookupError, Exception):
            pass

        # Try to load tenants from DB
        try:
            Tenant = apps.get_model("tenants", "Tenant")
            Domain = apps.get_model("tenants", "Domain")

            for tenant in Tenant.objects.filter(is_active=True):
                domains = {}
                for domain in Domain.objects.filter(tenant=tenant):
                    domain_type = getattr(domain, "domain_type", "primary")
                    domains[domain_type] = domain.domain

                features = []
                if hasattr(tenant, "features") and tenant.features:
                    features = [k for k, v in tenant.features.items() if v]

                entry = TenantEntry(
                    slug=tenant.slug,
                    display_name=getattr(tenant, "display_name", tenant.slug),
                    domains=domains,
                    features=features,
                )
                registry.tenants._tenants[tenant.slug] = entry

                for domain in entry.get_all_domains():
                    registry.tenants._domains_to_tenant[domain] = tenant.slug
        except (LookupError, Exception):
            pass

    except ImportError:
        # Django not available
        pass

    return registry


def build_registry(
    schema_dir: Path | None = None,
    use_db: bool = True,
) -> SSTRegistry:
    """
    Build a fresh registry from SST sources.

    Args:
        schema_dir: Path to YAML schema definitions (default: auto-detect)
        use_db: Whether to load from database (True) or YAML-only (False)

    Returns:
        Fully loaded SSTRegistry
    """
    loader = SchemaLoader(schema_dir=schema_dir)

    registry = SSTRegistry(
        routes=_load_routes_from_yaml(loader),
        webhooks=_load_webhooks_from_yaml(loader),
        tenants=_load_tenants_from_yaml(loader),
        layouts=_load_layouts_from_yaml(loader),
        blueprints=_load_blueprints_from_yaml(loader),
        user_types=_load_user_types_from_yaml(loader),
        user_flows=_load_user_flows_from_yaml(loader),
        packages=_load_packages_from_yaml(loader),
        _use_db=use_db,
    )

    # Override with database values if requested
    if use_db:
        registry = _load_from_database(registry)

    registry._loaded = True
    return registry


@lru_cache(maxsize=1)
def get_registry(use_db: bool = True) -> SSTRegistry:
    """
    Get the singleton SST registry.

    This is the main entry point for accessing SST values throughout
    the application. Uses LRU cache to ensure single instance.

    Args:
        use_db: Whether to load from database (default: True)
                Set to False in tests for deterministic YAML-only behavior

    Returns:
        The global SSTRegistry instance

    Usage:
        from lightwave.schema.core.registry import get_registry

        registry = get_registry()
        login_url = registry.routes.get_url("login")
    """
    return build_registry(use_db=use_db)


def clear_registry_cache() -> None:
    """
    Clear the registry cache.

    Call this when you need to reload the registry, such as
    after database migrations or YAML file changes.
    """
    get_registry.cache_clear()


# =============================================================================
# Convenience Functions
# =============================================================================


def get_route_url(name: str, **kwargs: Any) -> str:
    """
    Get URL for a named route.

    Convenience function that doesn't require importing the full registry.

    Usage:
        from lightwave.schema.core.registry import get_route_url

        login_url = get_route_url("login")
        team_url = get_route_url("team-home", team_slug="my-team")
    """
    return get_registry().routes.get_url(name, **kwargs)


def get_webhook_endpoint(provider: str, event: str | None = None) -> str:
    """
    Get webhook endpoint URL.

    Usage:
        from lightwave.schema.core.registry import get_webhook_endpoint

        stripe_url = get_webhook_endpoint("stripe")
        stripe_checkout = get_webhook_endpoint("stripe", "checkout.session.completed")
    """
    return get_registry().webhooks.get_endpoint(provider, event)


def get_tenant_domain(slug: str, domain_type: str = "primary") -> str | None:
    """
    Get domain for a tenant.

    Usage:
        from lightwave.schema.core.registry import get_tenant_domain

        cineos_domain = get_tenant_domain("cineos")  # "cineos.io"
        cineos_app = get_tenant_domain("cineos", "app")  # "app.cineos.io"
    """
    tenant = get_registry().tenants.get(slug)
    return tenant.get_domain(domain_type) if tenant else None


def is_valid_user_type(name: str) -> bool:
    """
    Check if a user type is valid according to SST.

    Usage:
        from lightwave.schema.core.registry import is_valid_user_type

        if is_valid_user_type("team_owner"):
            # Valid user type
            pass
    """
    return get_registry().user_types.is_valid(name)
