"""High-level SDK — developer-friendly wrapper that hides async tasks.

Usage::

    client = await FereClient.create("my-bot")
    result = await client.swap(
        chain_id_in=8453,
        chain_id_out=8453,
        token_in="0xEeee...EEeE",
        token_out="0x8335...2913",
        amount="1000000000000000000",
    )
    print(result)  # Final swap result, not a task_id
"""

from __future__ import annotations

from typing import Any

from .low_level import FereAPI
from .models import (
    DepositRequest,
    HooksRequest,
    LimitOrderRequest,
    SwapRequest,
    WithdrawRequest,
)

DEFAULT_TIMEOUT = 120


class FereClient:
    """High-level async client that blocks until tasks complete.

    All trading operations (swap, limit_order, deposit, withdraw)
    wait for the task to finish and return the final result.
    """

    def __init__(self, api: FereAPI) -> None:
        self._api = api

    @classmethod
    async def create(
        cls,
        agent_name: str,
        base_url: str = "https://api.fereai.xyz",
        key_path: str | None = None,
    ) -> FereClient:
        """Create and authenticate a high-level client.

        Generates a keypair (if needed), registers the agent,
        and obtains a bearer token — all automatically.
        """
        api = FereAPI(
            agent_name=agent_name,
            base_url=base_url,
            key_path=key_path,
        )
        await api.authenticate()
        return cls(api)

    async def close(self) -> None:
        await self._api.close()

    async def __aenter__(self):
        return self

    async def __aexit__(self, *exc):
        await self.close()

    # ----------------------------------------------------------
    # Chat
    # ----------------------------------------------------------

    async def chat(
        self,
        query: str,
        thread_id: str | None = None,
        agent: str = "ProAgent",
    ) -> dict:
        """Send a chat query and return the final answer.

        Returns dict with ``answer``, ``chat_id``, and
        optionally ``tool_responses``.
        """
        result: dict[str, Any] = {}
        tool_responses: list[dict] = []

        async for event in self._api.chat(
            query, thread_id=thread_id, agent=agent
        ):
            if event.event == "meta":
                result["chat_id"] = event.data.get("chat_id")
            elif event.event == "tool_response":
                tool_responses.append(event.data)
            elif event.event == "answer":
                result["answer"] = event.data.get("text", "")
            elif event.event == "done":
                result["done"] = event.data
            elif event.event == "error":
                raise RuntimeError(
                    event.data.get("message", str(event.data))
                )

        if tool_responses:
            result["tool_responses"] = tool_responses
        return result

    async def chat_stream(
        self,
        query: str,
        thread_id: str | None = None,
        agent: str = "ProAgent",
    ):
        """Stream chat events. Yields ChatEvent objects."""
        async for event in self._api.chat(
            query, thread_id=thread_id, agent=agent
        ):
            yield event

    # ----------------------------------------------------------
    # Trading (blocks until complete)
    # ----------------------------------------------------------

    async def swap(
        self,
        chain_id_in: int,
        chain_id_out: int,
        token_in: str,
        token_out: str,
        amount: str,
        slippage_bps: int = 50,
        timeout: int = DEFAULT_TIMEOUT,
        **kwargs,
    ) -> dict:
        """Execute a swap and wait for completion."""
        req = SwapRequest(
            chain_id_in=chain_id_in,
            chain_id_out=chain_id_out,
            token_in=token_in,
            token_out=token_out,
            amount=amount,
            slippage_bps=slippage_bps,
            **kwargs,
        )
        result = await self._api.create_swap(req, wait=True, timeout=timeout)
        return result

    async def limit_order(
        self,
        chain_id_in: int,
        chain_id_out: int,
        token_in: str,
        token_out: str,
        amount: str,
        price_usd_trigger: float,
        trigger_token_address: str,
        trigger_token_chain: str,
        condition: str,
        timeout: int = DEFAULT_TIMEOUT,
        **kwargs,
    ) -> dict:
        """Create a limit order and wait for setup completion."""
        req = LimitOrderRequest(
            chain_id_in=chain_id_in,
            chain_id_out=chain_id_out,
            token_in=token_in,
            token_out=token_out,
            amount=amount,
            price_usd_trigger=price_usd_trigger,
            trigger_token_address=trigger_token_address,
            trigger_token_chain=trigger_token_chain,
            condition=condition,
            **kwargs,
        )
        result = await self._api.create_limit_order(
            req, wait=True, timeout=timeout
        )
        return result

    async def set_hooks(
        self,
        chain_id: int,
        token_address: str,
        stop_loss: dict | None = None,
        take_profit: dict | None = None,
    ) -> dict:
        """Set stop-loss / take-profit hooks on a token."""
        req = HooksRequest(
            chain_id=chain_id,
            token_address=token_address,
            stop_loss=stop_loss,
            take_profit=take_profit,
        )
        return await self._api.set_hooks(req)

    # ----------------------------------------------------------
    # Earn (blocks until complete)
    # ----------------------------------------------------------

    async def deposit(
        self,
        amount_usdc: float,
        position_id: str | None = None,
        timeout: int = DEFAULT_TIMEOUT,
    ) -> dict:
        """Deposit USDC and wait for completion."""
        req = DepositRequest(
            amount_usdc=amount_usdc,
            position_id=position_id,
        )
        return await self._api.deposit(req, wait=True, timeout=timeout)

    async def withdraw(
        self,
        position_id: str,
        amount_usdc: float,
        timeout: int = DEFAULT_TIMEOUT,
    ) -> dict:
        """Withdraw USDC and wait for completion."""
        req = WithdrawRequest(position_id=position_id, amount_usdc=amount_usdc)
        return await self._api.withdraw(req, wait=True, timeout=timeout)

    # ----------------------------------------------------------
    # Read-only (pass-through)
    # ----------------------------------------------------------

    async def get_wallets(self) -> dict:
        return await self._api.get_wallets()

    async def get_holdings(self) -> dict:
        return await self._api.get_holdings()

    async def get_credits(self) -> float | None:
        info = await self._api.get_credits()
        return info.credits_available

    async def get_user(self) -> dict:
        return await self._api.get_user()

    async def get_chains(self) -> Any:
        return await self._api.get_chains()

    async def get_earn_info(self) -> dict:
        return await self._api.get_earn_info()

    async def get_positions(self) -> Any:
        return await self._api.get_positions()

    async def get_threads(self, skip: int = 0, limit: int = 10) -> list[dict]:
        return await self._api.get_threads(skip=skip, limit=limit)

    async def get_limit_orders(self, status: str | None = None) -> list[dict]:
        return await self._api.get_limit_orders(status=status)

    async def get_notifications(
        self,
        limit: int = 10,
        offset: int = 0,
        type: str | None = None,
    ) -> Any:
        return await self._api.get_notifications(
            limit=limit, offset=offset, type=type
        )
