from __future__ import annotations

import asyncio
from typing import Any, AsyncIterator, Iterable, Optional

from ..common.cpz_ai import CPZAIClient
from ..execution.models import (
    Account,
    Order,
    OrderReplaceRequest,
    OrderSubmitRequest,
    Position,
    Quote,
)
from ..execution.router import BrokerRouter
from ..simons import AsyncSimonsClient
from .base import BaseClient


class _AsyncExecutionNamespace:
    def __init__(self, router: BrokerRouter) -> None:
        self.router = router

    async def use_broker(
        self, name: str, environment: str = "paper", account_id: Optional[str] = None
    ) -> None:
        self.router.use_broker(name, environment=environment, account_id=account_id)

    def get_account(self) -> Account:
        return self.router.get_account()

    def get_positions(self) -> list[Position]:
        return self.router.get_positions()

    def submit_order(self, req: OrderSubmitRequest) -> Order:
        return self.router.submit_order(req)

    def get_order(self, order_id: str) -> Order:
        return self.router.get_order(order_id)

    def cancel_order(self, order_id: str) -> Order:
        return self.router.cancel_order(order_id)

    def replace_order(self, order_id: str, req: OrderReplaceRequest) -> Order:
        return self.router.replace_order(order_id, req)

    async def stream_quotes(self, symbols: Iterable[str]) -> AsyncIterator[Quote]:
        async for q in self.router.stream_quotes(symbols):
            yield q


class _AsyncPlatformNamespace:
    def __init__(self) -> None:
        self._sb: CPZAIClient | None = None

    async def configure(
        self, *, url: str | None = None, anon: str | None = None, service: str | None = None
    ) -> None:
        if url and anon:
            self._sb = CPZAIClient(url=url, api_key=anon, secret_key=service or "")
        else:
            self._sb = CPZAIClient.from_env()

    def _require(self) -> CPZAIClient:
        if self._sb is None:
            self._sb = CPZAIClient.from_env()
        return self._sb

    async def health(self) -> bool:
        return self._require().health()

    async def echo(self) -> dict[str, object]:
        return self._require().echo()

    async def list_tables(self) -> list[str]:
        return self._require().list_tables()


class AsyncCPZClient(BaseClient):
    def __init__(self, cpz_client: Optional[CPZAIClient] = None) -> None:
        super().__init__()
        self._cpz_client = cpz_client or CPZAIClient.from_env()
        self.execution = _AsyncExecutionNamespace(
            BrokerRouter.default().with_cpz_client(self._cpz_client)
        )
        self.platform = _AsyncPlatformNamespace()
        # Simons AI Assistant client (lazy-loaded)
        self._simons: Optional[AsyncSimonsClient] = None

    @property
    def router(self) -> BrokerRouter:
        return self.execution.router
    
    @property
    def simons(self) -> AsyncSimonsClient:
        """Access Simons AI Assistant (async) for trading analysis and strategy development.
        
        See CPZClient.simons for full documentation. This is the async version
        that can be used with asyncio.
        
        Example:
            response = await client.simons.chat("Analyze AAPL")
            print(response.content)
            
            async for chunk in client.simons.stream("Write a backtest"):
                print(chunk.content, end="", flush=True)
        """
        if self._simons is None:
            self._simons = AsyncSimonsClient(self._cpz_client)
        return self._simons

    # File operations - delegate to CPZAIClient (run in thread pool for async)
    async def upload_dataframe(
        self, bucket_name: str, file_path: str, df: Any, format: str = "csv", **kwargs
    ) -> Optional[dict[str, Any]]:
        """Upload a pandas DataFrame to storage"""
        return await asyncio.to_thread(
            lambda: self._cpz_client.upload_dataframe(
                bucket_name, file_path, df, format=format, **kwargs
            )
        )

    async def download_csv_to_dataframe(
        self, bucket_name: str, file_path: str, encoding: str = "utf-8", **kwargs
    ) -> Optional[Any]:
        """Download a CSV file and load it into a pandas DataFrame"""
        return await asyncio.to_thread(
            lambda: self._cpz_client.download_csv_to_dataframe(
                bucket_name, file_path, encoding=encoding, **kwargs
            )
        )

    async def download_json_to_dataframe(
        self, bucket_name: str, file_path: str, **kwargs
    ) -> Optional[Any]:
        """Download a JSON file and load it into a pandas DataFrame"""
        return await asyncio.to_thread(
            lambda: self._cpz_client.download_json_to_dataframe(bucket_name, file_path, **kwargs)
        )

    async def download_parquet_to_dataframe(
        self, bucket_name: str, file_path: str, **kwargs
    ) -> Optional[Any]:
        """Download a Parquet file and load it into a pandas DataFrame"""
        return await asyncio.to_thread(
            lambda: self._cpz_client.download_parquet_to_dataframe(
                bucket_name, file_path, **kwargs
            )
        )

    async def list_files_in_bucket(
        self, bucket_name: str, prefix: str = "", limit: int = 100
    ) -> list[dict[str, Any]]:
        """List files in a storage bucket with optional prefix filtering"""
        return await asyncio.to_thread(
            lambda: self._cpz_client.list_files_in_bucket(bucket_name, prefix=prefix, limit=limit)
        )

    async def delete_file(self, bucket_name: str, file_path: str) -> bool:
        """Delete a file from storage"""
        return await asyncio.to_thread(
            lambda: self._cpz_client.delete_file(bucket_name, file_path)
        )
