"""
Staking resource clients (sync and async) for deposit, withdraw,
query, and calculate operations.
"""

from __future__ import annotations

from typing import Any
from urllib.parse import quote

from ..auth import sign_deposit, sign_withdraw
from ..http import HttpClient, AsyncHttpClient
from ..models import StakeInfo, StakeCalculation


class StakingClient:
    """Synchronous client for staking endpoints."""

    def __init__(self, http: HttpClient, private_key: str | None = None) -> None:
        self._http = http
        self._private_key = private_key

    def deposit(self, agent_id: str, amount: str) -> StakeInfo:
        """
        Deposit a stake for an agent. Staking increases the agent's
        effective reputation and unlocks higher-tier functionality.
        Requires a private key.
        """
        if not self._private_key:
            raise ValueError("A private_key is required to deposit a stake.")

        sig_data = sign_deposit(self._private_key, agent_id, amount)

        data = self._http.post(
            f"/v1/staking/{quote(agent_id, safe='')}/deposit",
            {
                "amount": amount,
                "signature": sig_data["signature"],
                "nonce": sig_data["nonce"],
                "deadline": sig_data["deadline"],
            },
        )
        return StakeInfo.model_validate(data)

    def withdraw(self, agent_id: str, amount: str) -> StakeInfo:
        """
        Withdraw a portion or all of an agent's stake.
        Subject to lock-up periods and minimum stake requirements.
        Requires a private key.
        """
        if not self._private_key:
            raise ValueError("A private_key is required to withdraw a stake.")

        sig_data = sign_withdraw(self._private_key, agent_id, amount)

        data = self._http.post(
            f"/v1/staking/{quote(agent_id, safe='')}/withdraw",
            {
                "amount": amount,
                "signature": sig_data["signature"],
                "nonce": sig_data["nonce"],
                "deadline": sig_data["deadline"],
            },
        )
        return StakeInfo.model_validate(data)

    def get(self, agent_id: str) -> StakeInfo:
        """Get the current staking information for an agent."""
        data = self._http.get(f"/v1/staking/{quote(agent_id, safe='')}")
        return StakeInfo.model_validate(data)

    def calculate(self, agent_id: str) -> StakeCalculation:
        """
        Calculate the staking requirements and reputation bonus for an agent.
        Useful for understanding how much stake is needed to reach a target
        effective reputation.
        """
        data = self._http.get(
            f"/v1/staking/{quote(agent_id, safe='')}/calculate"
        )
        return StakeCalculation.model_validate(data)


class AsyncStakingClient:
    """Asynchronous client for staking endpoints."""

    def __init__(self, http: AsyncHttpClient, private_key: str | None = None) -> None:
        self._http = http
        self._private_key = private_key

    async def deposit(self, agent_id: str, amount: str) -> StakeInfo:
        """
        Deposit a stake for an agent. Staking increases the agent's
        effective reputation and unlocks higher-tier functionality.
        Requires a private key.
        """
        if not self._private_key:
            raise ValueError("A private_key is required to deposit a stake.")

        sig_data = sign_deposit(self._private_key, agent_id, amount)

        data = await self._http.post(
            f"/v1/staking/{quote(agent_id, safe='')}/deposit",
            {
                "amount": amount,
                "signature": sig_data["signature"],
                "nonce": sig_data["nonce"],
                "deadline": sig_data["deadline"],
            },
        )
        return StakeInfo.model_validate(data)

    async def withdraw(self, agent_id: str, amount: str) -> StakeInfo:
        """
        Withdraw a portion or all of an agent's stake.
        Subject to lock-up periods and minimum stake requirements.
        Requires a private key.
        """
        if not self._private_key:
            raise ValueError("A private_key is required to withdraw a stake.")

        sig_data = sign_withdraw(self._private_key, agent_id, amount)

        data = await self._http.post(
            f"/v1/staking/{quote(agent_id, safe='')}/withdraw",
            {
                "amount": amount,
                "signature": sig_data["signature"],
                "nonce": sig_data["nonce"],
                "deadline": sig_data["deadline"],
            },
        )
        return StakeInfo.model_validate(data)

    async def get(self, agent_id: str) -> StakeInfo:
        """Get the current staking information for an agent."""
        data = await self._http.get(f"/v1/staking/{quote(agent_id, safe='')}")
        return StakeInfo.model_validate(data)

    async def calculate(self, agent_id: str) -> StakeCalculation:
        """
        Calculate the staking requirements and reputation bonus for an agent.
        Useful for understanding how much stake is needed to reach a target
        effective reputation.
        """
        data = await self._http.get(
            f"/v1/staking/{quote(agent_id, safe='')}/calculate"
        )
        return StakeCalculation.model_validate(data)
