"""
Accounts client for Daisi SDK.
"""

from typing import Optional

import grpc

from protos.v1 import Accounts_pb2, Accounts_pb2_grpc
from protos.v1.models import AccountModels_pb2

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


class AccountClient(BaseClient):
    """Client for account and user management operations."""

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

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

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

    def get_account(self) -> AccountModels_pb2.GetAccountResponse:
        """Get the current account.

        Returns:
            Account response with account info and tax ID status.

        Raises:
            DaisiError: If request fails.
        """
        request = AccountModels_pb2.GetAccountRequest()

        try:
            return self.stub.GetAccount(request, metadata=self._get_metadata())
        except grpc.RpcError as e:
            raise DaisiError(f"Failed to get account: {e.details()}") from e

    def update_account(
        self, account: AccountModels_pb2.Account
    ) -> tuple[bool, Optional[str]]:
        """Update account information.

        Args:
            account: Account object with updated information.

        Returns:
            Tuple of (success, message).

        Raises:
            DaisiError: If update fails.
        """
        request = AccountModels_pb2.UpdateAccountRequest(Account=account)

        try:
            response = self.stub.UpdateAccount(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 account: {e.details()}") from e

    def get_users(
        self,
        page_index: Optional[int] = None,
        page_size: Optional[int] = None,
        search_term: Optional[str] = None,
    ) -> tuple[list[AccountModels_pb2.User], int]:
        """Get users in the account.

        Args:
            page_index: Page index for pagination.
            page_size: Number of items per page.
            search_term: Search term to filter users.

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

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

        request = AccountModels_pb2.GetUsersRequest(Paging=paging)

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

    def get_user(self, user_id: str) -> Optional[AccountModels_pb2.User]:
        """Get a specific user by ID.

        Args:
            user_id: The user ID.

        Returns:
            User object if found, None otherwise.

        Raises:
            DaisiError: If request fails.
        """
        request = AccountModels_pb2.GetUserRequest(UserId=user_id)

        try:
            response = self.stub.GetUser(request, metadata=self._get_metadata())
            if response.Success:
                return response.User
            return None
        except grpc.RpcError as e:
            raise DaisiError(f"Failed to get user: {e.details()}") from e

    def create_user(
        self,
        user: AccountModels_pb2.User,
        account_name: Optional[str] = None,
    ) -> tuple[bool, Optional[AccountModels_pb2.User], Optional[str]]:
        """Create a new user.

        Args:
            user: User object to create.
            account_name: Optional account name.

        Returns:
            Tuple of (success, created user, message).

        Raises:
            DaisiError: If creation fails.
        """
        request = AccountModels_pb2.CreateUserRequest(User=user)
        if account_name:
            request.AccountName = account_name

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

    def update_user(self, user: AccountModels_pb2.User) -> tuple[bool, Optional[str]]:
        """Update a user.

        Args:
            user: User object with updated information.

        Returns:
            Tuple of (success, message).

        Raises:
            DaisiError: If update fails.
        """
        request = AccountModels_pb2.UpdateUserRequest(User=user)

        try:
            response = self.stub.UpdateUser(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 user: {e.details()}") from e

    def archive_user(self, user_id: str) -> tuple[bool, Optional[str]]:
        """Archive a user.

        Args:
            user_id: ID of the user to archive.

        Returns:
            Tuple of (success, message).

        Raises:
            DaisiError: If archive fails.
        """
        request = AccountModels_pb2.ArchiveUserRequest(UserId=user_id)

        try:
            response = self.stub.ArchiveUser(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 user: {e.details()}") from e
