"""Lightweight domain decorators for declarative business metadata."""

from __future__ import annotations

from dataclasses import dataclass
from typing import Any, Callable, TypeVar

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


@dataclass(frozen=True)
class DecoratorMetadata:
    kind: str
    fields: tuple[str, ...] = ()
    name: str | None = None


def _validate_fields(fields: tuple[str, ...], decorator_name: str) -> tuple[str, ...]:
    if not fields:
        raise ValueError(f"{decorator_name} requires at least one field name")
    normalized: list[str] = []
    for field in fields:
        if not isinstance(field, str) or not field.strip():
            raise ValueError(f"{decorator_name} field names must be non-empty strings")
        normalized.append(field.strip())
    return tuple(normalized)


def _attach_metadata(func: F, metadata: DecoratorMetadata) -> F:
    existing = list(getattr(func, "__domain_decorators__", ()))
    existing.append(metadata)
    setattr(func, "__domain_decorators__", tuple(existing))
    return func


def _decorator(kind: str, *fields: str, name: str | None = None) -> Callable[[F], F]:
    normalized_fields = _validate_fields(tuple(fields), kind)

    def decorator(func: F) -> F:
        return _attach_metadata(func, DecoratorMetadata(kind=kind, fields=normalized_fields, name=name))

    return decorator


def depends(*fields: str) -> Callable[[F], F]:
    """Declare fields that a computed method depends on."""

    return _decorator("depends", *fields)


def constrains(*fields: str) -> Callable[[F], F]:
    """Declare fields that must be validated together."""

    return _decorator("constrains", *fields)


def onchange(*fields: str) -> Callable[[F], F]:
    """Declare fields that should trigger a reactive update."""

    return _decorator("onchange", *fields)


def hook(*fields: str, name: str | None = None) -> Callable[[F], F]:
    """Declare a generic business hook for lifecycle integration."""

    return _decorator("hook", *fields, name=name)


def get_decorator_metadata(func: Callable[..., Any]) -> tuple[DecoratorMetadata, ...]:
    """Return all metadata attached by domain decorators."""

    return tuple(getattr(func, "__domain_decorators__", ()))


def has_decorator(func: Callable[..., Any], kind: str) -> bool:
    """Check whether a callable has a specific domain decorator kind."""

    return any(metadata.kind == kind for metadata in get_decorator_metadata(func))
