"""
Orchestrators (Orcs) client for Daisi SDK.
"""

from typing import Optional

import grpc

from protos.v1 import Orcs_pb2, Orcs_pb2_grpc
from protos.v1.models import OrcModels_pb2, SystemModels_pb2

from ..base_client import BaseClient
from ..config import DaisiConfig
from ..exceptions import DaisiError


class OrcClient(BaseClient):
    """Client for orchestrator management operations."""

    def __init__(self, config: Optional[DaisiConfig] = None) -> None:
        """Initialize orc client.

        Args:
            config: Daisi configuration.
        """
        super().__init__(config)
        self._stub: Optional[Orcs_pb2_grpc.OrcsProtoStub] = None

    @property
    def stub(self) -> Orcs_pb2_grpc.OrcsProtoStub:
        """Get or create the gRPC stub."""
        if self._stub is None:
            self._stub = Orcs_pb2_grpc.OrcsProtoStub(self._get_channel())
        return self._stub

    def get(
        self,
        orc_id: Optional[str] = None,
        network_id: Optional[str] = None,
        page_index: Optional[int] = None,
        page_size: Optional[int] = None,
    ) -> tuple[list[OrcModels_pb2.Orchestrator], int]:
        """Get orchestrators.

        Args:
            orc_id: Filter by specific orchestrator ID.
            network_id: Filter by network ID.
            page_index: Page index for pagination.
            page_size: Number of items per page.

        Returns:
            Tuple of (orchestrators list, total count).

        Raises:
            DaisiError: If request fails.
        """
        paging = SystemModels_pb2.PagingInfo()
        if page_index is not None:
            paging.PageIndex = page_index
        if page_size is not None:
            paging.PageSize = page_size

        request = OrcModels_pb2.GetOrcsRequest(Paging=paging)
        if orc_id:
            request.OrcId = orc_id
        if network_id:
            request.NetworkId = network_id

        try:
            response = self.stub.Get(request, metadata=self._get_metadata())
            return list(response.Orcs), response.TotalCount
        except grpc.RpcError as e:
            raise DaisiError(f"Failed to get orchestrators: {e.details()}") from e

    def create(
        self, orc: OrcModels_pb2.Orchestrator
    ) -> tuple[Optional[OrcModels_pb2.Orchestrator], Optional[str]]:
        """Create a new orchestrator.

        Args:
            orc: Orchestrator object to create.

        Returns:
            Tuple of (created orchestrator, message).

        Raises:
            DaisiError: If creation fails.
        """
        request = OrcModels_pb2.CreateOrcRequest(Orc=orc)

        try:
            response = self.stub.Create(request, metadata=self._get_metadata())
            return (
                response.Orc if response.HasField("Orc") else None,
                response.Message if response.Message else None,
            )
        except grpc.RpcError as e:
            raise DaisiError(f"Failed to create orchestrator: {e.details()}") from e

    def update(self, orc: OrcModels_pb2.Orchestrator) -> tuple[bool, Optional[str]]:
        """Update an orchestrator.

        Args:
            orc: Orchestrator object with updated information.

        Returns:
            Tuple of (success, message).

        Raises:
            DaisiError: If update fails.
        """
        request = OrcModels_pb2.UpdateOrcRequest(Orc=orc)

        try:
            response = self.stub.Update(request, metadata=self._get_metadata())
            return response.Success, response.Message if response.Message else None
        except grpc.RpcError as e:
            raise DaisiError(f"Failed to update orchestrator: {e.details()}") from e

    def archive(self, orc_id: str) -> tuple[bool, Optional[str]]:
        """Archive an orchestrator.

        Args:
            orc_id: ID of the orchestrator to archive.

        Returns:
            Tuple of (success, message).

        Raises:
            DaisiError: If archive fails.
        """
        request = OrcModels_pb2.ArchiveOrcRequest(OrcId=orc_id)

        try:
            response = self.stub.Archive(request, metadata=self._get_metadata())
            return response.Success, response.Message if response.Message else None
        except grpc.RpcError as e:
            raise DaisiError(f"Failed to archive orchestrator: {e.details()}") from e
