"""
Dapps (Distributed Apps) client for Daisi SDK.
"""

from typing import Optional

import grpc

from protos.v1 import Dapps_pb2, Dapps_pb2_grpc
from protos.v1.models import DappModels_pb2, SystemModels_pb2

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


class DappClient(BaseClient):
    """Client for distributed app (dapp) management operations."""

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

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

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

    def get(
        self,
        dapp_id: Optional[str] = None,
        page_index: Optional[int] = None,
        page_size: Optional[int] = None,
    ) -> tuple[list[DappModels_pb2.Dapp], int]:
        """Get dapps.

        Args:
            dapp_id: Filter by specific dapp ID.
            page_index: Page index for pagination.
            page_size: Number of items per page.

        Returns:
            Tuple of (dapps 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 = DappModels_pb2.GetDappRequest(Paging=paging)
        if dapp_id:
            request.DappId = dapp_id

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

    def create(
        self,
        name: str,
        dapp_type: DappModels_pb2.DappTypes = DappModels_pb2.DappConsumer,
    ) -> DappModels_pb2.Dapp:
        """Create a new dapp.

        Args:
            name: Name of the dapp.
            dapp_type: Type of dapp (Consumer or Tool).

        Returns:
            Created Dapp object.

        Raises:
            DaisiError: If creation fails.
        """
        request = DappModels_pb2.CreateDappRequest(Name=name, Type=dapp_type)

        try:
            response = self.stub.Create(request, metadata=self._get_metadata())
            return response.Dapp
        except grpc.RpcError as e:
            raise DaisiError(f"Failed to create dapp: {e.details()}") from e

    def update(self, dapp: DappModels_pb2.Dapp) -> tuple[bool, str]:
        """Update a dapp.

        Args:
            dapp: Dapp object with updated information.

        Returns:
            Tuple of (success, message).

        Raises:
            DaisiError: If update fails.
        """
        request = DappModels_pb2.UpdateDappRequest(Dapp=dapp)

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

    def delete(self, dapp_id: str) -> tuple[bool, str]:
        """Delete a dapp.

        Args:
            dapp_id: ID of the dapp to delete.

        Returns:
            Tuple of (success, message).

        Raises:
            DaisiError: If delete fails.
        """
        request = DappModels_pb2.DeleteDappRequest(DappId=dapp_id)

        try:
            response = self.stub.Delete(request, metadata=self._get_metadata())
            return response.Success, response.Message
        except grpc.RpcError as e:
            raise DaisiError(f"Failed to delete dapp: {e.details()}") from e

    def reset(self, dapp_id: str) -> str:
        """Reset a dapp and get a new secret key.

        Args:
            dapp_id: ID of the dapp to reset.

        Returns:
            New secret key.

        Raises:
            DaisiError: If reset fails.
        """
        request = DappModels_pb2.ResetDappRequest(DappId=dapp_id)

        try:
            response = self.stub.Reset(request, metadata=self._get_metadata())
            return response.SecretKey
        except grpc.RpcError as e:
            raise DaisiError(f"Failed to reset dapp: {e.details()}") from e

    def get_secret_key(self, dapp_id: str) -> str:
        """Get the secret key for a dapp.

        Args:
            dapp_id: ID of the dapp.

        Returns:
            Secret key.

        Raises:
            DaisiError: If request fails.
        """
        request = DappModels_pb2.GetDappSecretKeyRequest(DappId=dapp_id)

        try:
            response = self.stub.GetSecretKey(request, metadata=self._get_metadata())
            return response.SecretKey
        except grpc.RpcError as e:
            raise DaisiError(f"Failed to get dapp secret key: {e.details()}") from e
