"""
Capacity management for Microsoft Fabric.

Provides functionality for managing Fabric capacities including
workspace assignments and capacity settings.

Requires: Capacity Admin or Fabric Administrator permissions
"""

from typing import TYPE_CHECKING, Any, Callable, Dict, List, Optional, Union

from ..._shared.exceptions import FabiasError, NotFoundError
from ..._shared.utils import GUID

if TYPE_CHECKING:
    from .._client import FabricClient
    from ..workspace import Workspace


class Capacity:
    """
    Represents a Fabric capacity.

    Capacities provide compute and storage resources for Fabric workspaces.

    Attributes:
        id (str): Capacity GUID
        display_name (str): Capacity display name
        sku (str): Capacity SKU (e.g., "F2", "F4", "P1")
        region (str): Azure region
        state (str): Capacity state (Active, Paused, etc.)
        admins (list): List of capacity administrators

    Examples:
        >>> capacity = fabric.capacity("Premium-P1")
        >>> print(f"SKU: {capacity.sku}, Region: {capacity.region}")
        >>>
        >>> # Manage workspace assignments
        >>> ws = fabric.workspace("Analytics")
        >>> capacity.addWorkspace(ws)
        >>> capacity.removeWorkspace(ws)
    """

    def __init__(
        self,
        client: "FabricClient",
        identifier: Optional[str] = None,
        capacity_data: Optional[Dict[str, Any]] = None,
    ):
        """
        Initialize Capacity.

        Args:
            client: Authenticated FabricClient
            identifier: Capacity name or GUID (triggers lazy loading or immediate resolution)
            capacity_data: Pre-populated capacity data from API (skips resolution)
        """
        self._client = client
        self._data_fetched = False

        # Attributes (lazy loaded)
        self._id: Optional[str] = None
        self._display_name: Optional[str] = None
        self._sku: Optional[str] = None
        self._region: Optional[str] = None
        self._state: Optional[str] = None
        self._admins: Optional[List[str]] = None

        # Pre-populated from API response
        if capacity_data:
            self._id = capacity_data.get("id")
            self._display_name = capacity_data.get("displayName")
            self._sku = capacity_data.get("sku")
            self._region = capacity_data.get("region")
            self._state = capacity_data.get("state")
            self._admins = capacity_data.get("admins", [])
            self._data_fetched = True
            return

        # GUID provided - store and return (no API call)
        if identifier and GUID.valid(identifier):
            self._id = identifier
            return

        # Name provided - resolve to GUID (API call happens here)
        if identifier:
            self._resolve_identifier(identifier)

    def _resolve_identifier(self, name: str) -> None:
        """
        Resolve capacity name to GUID.

        Args:
            name: Capacity display name
        """
        # List all capacities and find matching name
        response = self._client.get("/admin/capacities")
        data = response.json()
        capacities = data.get("value", [])

        matches = [c for c in capacities if c.get("displayName") == name]

        if not matches:
            raise NotFoundError(
                f"Capacity '{name}' not found", resource_type="Capacity", resource_id=name
            )

        if len(matches) > 1:
            raise FabiasError(f"Multiple capacities found with name '{name}'")

        matched = matches[0]
        self._id = matched.get("id")
        self._display_name = matched.get("displayName")
        self._sku = matched.get("sku")
        self._region = matched.get("region")
        self._state = matched.get("state")
        self._admins = matched.get("admins", [])
        self._data_fetched = True

    def _fetch(self) -> None:
        """Lazy load capacity data from API if not already fetched."""
        if self._data_fetched or not self._id:
            return

        response = self._client.get(f"/admin/capacities/{self._id}")
        data = response.json()

        self._display_name = data.get("displayName")
        self._sku = data.get("sku")
        self._region = data.get("region")
        self._state = data.get("state")
        self._admins = data.get("admins", [])
        self._data_fetched = True

    @property
    def id(self) -> Optional[str]:
        """Capacity GUID."""
        return self._id

    @property
    def displayName(self) -> Optional[str]:
        """Capacity display name (lazy loaded)."""
        self._fetch()
        return self._display_name

    @property
    def sku(self) -> Optional[str]:
        """Capacity SKU (lazy loaded)."""
        self._fetch()
        return self._sku

    @property
    def region(self) -> Optional[str]:
        """Azure region (lazy loaded)."""
        self._fetch()
        return self._region

    @property
    def state(self) -> Optional[str]:
        """Capacity state (lazy loaded)."""
        self._fetch()
        return self._state

    @property
    def admins(self) -> Optional[List[str]]:
        """List of capacity administrator IDs (lazy loaded)."""
        self._fetch()
        return self._admins

    def refresh(self) -> "Capacity":
        """
        Force refresh capacity data from API.

        Returns:
            Capacity: Self for method chaining
        """
        self._data_fetched = False
        self._fetch()
        return self

    def workspaces(self) -> List["Workspace"]:
        """
        List all workspaces assigned to this capacity.

        Returns:
            list[Workspace]: Workspaces in this capacity

        Examples:
            >>> capacity = fabric.capacity("Premium-P1")
            >>> for ws in capacity.workspaces():
            ...     print(ws.name)
        """
        from ..workspace import Workspace

        response = self._client.get(f"/admin/capacities/{self.id}/workspaces")
        data = response.json()
        workspaces_data = data.get("value", [])

        return [Workspace(self._client, workspace_data=ws_data) for ws_data in workspaces_data]

    def addWorkspace(self, workspace: "Workspace") -> None:
        """
        Assign a workspace to this capacity.

        Args:
            workspace: Workspace object to assign

        Examples:
            >>> capacity = fabric.capacity("Premium-P1")
            >>> ws = fabric.workspace("Analytics")
            >>> capacity.addWorkspace(ws)
        """
        payload = {"capacityId": self.id}
        self._client.post(f"/workspaces/{workspace.id}/assignToCapacity", data=payload)

    def removeWorkspace(self, workspace: "Workspace") -> None:
        """
        Unassign a workspace from this capacity.

        Args:
            workspace: Workspace object to unassign

        Examples:
            >>> capacity = fabric.capacity("Premium-P1")
            >>> ws = fabric.workspace("Analytics")
            >>> capacity.removeWorkspace(ws)
        """
        self._client.post(f"/workspaces/{workspace.id}/unassignFromCapacity")

    def __repr__(self) -> str:
        return f"Capacity(id={self.id}, name={self.displayName}, sku={self.sku})"


class CapacitiesAccessor:
    """Accessor for Capacity list/retrieval operations."""

    def __init__(self, client_or_getter: Union["FabricClient", Callable[[], "FabricClient"]]):
        """
        Args:
            client_or_getter: FabricClient or callable returning one (for lazy loading)
        """
        if callable(client_or_getter):
            self._client_getter: Optional[Callable[[], "FabricClient"]] = client_or_getter
            self._client: Optional["FabricClient"] = None
        else:
            self._client = client_or_getter
            self._client_getter = None

    def _get_client(self) -> "FabricClient":
        """Get client, lazily loading if needed."""
        if self._client is None:
            if self._client_getter is None:
                raise RuntimeError("No client or client getter available")
            self._client = self._client_getter()
        return self._client

    def __call__(self, name: Optional[str] = None) -> List[Capacity]:
        """
        List all capacities, optionally filtered by name.

        Args:
            name: Optional name filter (case-insensitive substring match)

        Returns:
            list[Capacity]: List of Capacity objects

        Examples:
            >>> capacities = fabric.capacities()
            >>> premium_caps = fabric.capacities(name="Premium")
        """
        response = self._get_client().get("/admin/capacities")
        data = response.json()
        capacities_data = data.get("value", [])

        capacities = [
            Capacity(self._get_client(), capacity_data=cap_data) for cap_data in capacities_data
        ]

        # Filter by name if provided
        if name:
            name_lower = name.lower()
            capacities = [c for c in capacities if name_lower in (c.displayName or "").lower()]

        return capacities
