"""Virtual resource type base class and implementations.

Virtual resources represent abstract or computed resources that don't map to
physical files. Examples include computed metrics, API endpoints, or derived data.

This module provides:
- :class:`VirtualResource` — base class for all virtual resources
- :class:`MetricResource` — example: computed metrics
- :class:`APIEndpointResource` — example: API endpoints

Based on:
- docs/specification.md — Virtual Resource Types
- Issue #8610 — Virtual Resource Type Base Class Implementation
"""

from __future__ import annotations

import logging
import re
from collections.abc import Callable
from typing import Any

logger = logging.getLogger(__name__)

_NAME_PATTERN = re.compile(r"^[a-zA-Z][a-zA-Z0-9_-]*$")


class VirtualResource[T]:
    """Base class for virtual (computed/abstract) resources.

    Virtual resources are abstract or computed resources that don't map to
    physical files. They can be computed on demand via a compute function.

    Attributes:
        name: Unique identifier for this virtual resource.
        description: Human-readable description of the resource.
        compute_fn: Callable that computes the resource value on demand.
    """

    def __init__(
        self,
        name: str,
        description: str,
        compute_fn: Callable[..., T],
    ) -> None:
        """Initialize a virtual resource.

        Args:
            name: Unique identifier for this virtual resource.
                Must be a valid resource name (alphanumeric, hyphens, underscores).
            description: Human-readable description of what this resource computes.
            compute_fn: Callable that computes the resource value on demand.
                Should accept no required arguments (all args should have defaults).

        Raises:
            ValueError: If name is empty or invalid.
            TypeError: If compute_fn is not callable.
        """
        if not name or not isinstance(name, str):
            raise ValueError(
                f"Resource name must be a non-empty string, got {type(name).__name__}"
            )

        if not _NAME_PATTERN.match(name):
            raise ValueError(
                f"Resource name '{name}' contains invalid characters. "
                "Names must start with a letter and contain only "
                "alphanumeric characters, hyphens, and underscores."
            )

        if not callable(compute_fn):
            raise TypeError(
                f"compute_fn must be callable, got {type(compute_fn).__name__}"
            )

        self.name: str = name
        self.description: str = description
        self.compute_fn: Callable[..., T] = compute_fn

    def compute(self, **kwargs: Any) -> T:
        """Compute the resource value on demand.

        Args:
            **kwargs: Optional keyword arguments to pass to the compute function.

        Returns:
            The computed resource value.

        Raises:
            Exception: Any exception raised by the compute function.
        """
        logger.debug(
            "Computing virtual resource",
            extra={
                "resource_name": self.name,
                "kwargs_keys": list(kwargs.keys()),
            },
        )
        return self.compute_fn(**kwargs)

    def __repr__(self) -> str:
        """Return string representation of the virtual resource."""
        return (
            f"{self.__class__.__name__}("
            f"name={self.name!r}, "
            f"description={self.description!r})"
        )


class MetricResource(VirtualResource[float]):
    """Virtual resource that computes a metric value.

    Example:
        >>> def compute_cpu_usage() -> float:
        ...     # Compute CPU usage percentage
        ...     return 45.2
        >>> metric = MetricResource(
        ...     name="cpu-usage",
        ...     description="Current CPU usage percentage",
        ...     compute_fn=compute_cpu_usage,
        ... )
        >>> value = metric.compute()
        >>> print(f"CPU Usage: {value}%")
        CPU Usage: 45.2%
    """

    def __init__(
        self,
        name: str,
        description: str,
        compute_fn: Callable[..., float],
        unit: str = "",
    ) -> None:
        """Initialize a metric resource.

        Args:
            name: Unique identifier for this metric.
            description: Human-readable description of the metric.
            compute_fn: Callable that computes the metric value (returns float).
            unit: Optional unit of measurement (e.g., "percent", "bytes", "ms").
        """
        super().__init__(name, description, compute_fn)
        self.unit: str = unit

    def __repr__(self) -> str:
        """Return string representation of the metric resource."""
        unit_str = f", unit={self.unit!r}" if self.unit else ""
        return (
            f"MetricResource("
            f"name={self.name!r}, "
            f"description={self.description!r}{unit_str})"
        )


class APIEndpointResource(VirtualResource[dict[str, Any]]):
    """Virtual resource that represents an API endpoint.

    Example:
        >>> def fetch_api_data() -> dict[str, Any]:
        ...     # Fetch data from an API
        ...     return {"status": "ok", "data": [1, 2, 3]}
        >>> endpoint = APIEndpointResource(
        ...     name="api-data",
        ...     description="Data from external API",
        ...     compute_fn=fetch_api_data,
        ...     endpoint_url="https://api.example.com/data",
        ... )
        >>> data = endpoint.compute()
        >>> print(data["status"])
        ok
    """

    def __init__(
        self,
        name: str,
        description: str,
        compute_fn: Callable[..., dict[str, Any]],
        endpoint_url: str = "",
    ) -> None:
        """Initialize an API endpoint resource.

        Args:
            name: Unique identifier for this endpoint.
            description: Human-readable description of the endpoint.
            compute_fn: Callable that fetches data from the endpoint
                (returns dict).
            endpoint_url: Optional URL of the API endpoint.
        """
        super().__init__(name, description, compute_fn)
        self.endpoint_url: str = endpoint_url

    def __repr__(self) -> str:
        """Return string representation of the API endpoint resource."""
        url_str = f", endpoint_url={self.endpoint_url!r}" if self.endpoint_url else ""
        return (
            f"APIEndpointResource("
            f"name={self.name!r}, "
            f"description={self.description!r}{url_str})"
        )


__all__ = [
    "APIEndpointResource",
    "MetricResource",
    "VirtualResource",
]
