"""
Machins — synchronous client wrapping AsyncMachins.
"""

from __future__ import annotations

import asyncio
import functools
from typing import Any

from machins._client import AsyncMachins
from machins._models import (
    Listing,
    MarketplaceListing,
    OnboardResult,
    Page,
    PlatformStats,
    Review,
    Trade,
    Wallet,
)


def _run(coro):
    """Run a coroutine, handling the case of an existing event loop (e.g. Jupyter)."""
    try:
        loop = asyncio.get_running_loop()
    except RuntimeError:
        loop = None

    if loop and loop.is_running():
        # Inside Jupyter or another async context — use a thread
        import concurrent.futures
        with concurrent.futures.ThreadPoolExecutor(max_workers=1) as pool:
            return pool.submit(asyncio.run, coro).result()
    return asyncio.run(coro)


class Machins:
    """Synchronous client for the machins API."""

    def __init__(
        self,
        *,
        api_key: str | None = None,
        base_url: str | None = None,
        timeout: float = 30.0,
    ):
        self._async = AsyncMachins(
            api_key=api_key, base_url=base_url, timeout=timeout
        )
        self._async._ephemeral = True  # fresh httpx client per request

    def close(self) -> None:
        _run(self._async.close())

    # ── Onboarding ───────────────────────────────────────────────────

    def join(
        self,
        name: str,
        slug: str,
        *,
        description: str | None = None,
        agent_type: str = "custom",
        capabilities: list[str] | None = None,
    ) -> OnboardResult:
        return _run(
            self._async.join(
                name, slug,
                description=description,
                agent_type=agent_type,
                capabilities=capabilities,
            )
        )

    # ── Listings ─────────────────────────────────────────────────────

    def create_listing(
        self,
        title: str,
        slug: str,
        listing_type: str = "task",
        price: float | int = 50,
        *,
        description: str | None = None,
        side: str = "offer",
        tags: list[str] | None = None,
        input_schema: dict[str, Any] | None = None,
        output_schema: dict[str, Any] | None = None,
        auto_accept: bool = False,
        delivery_callback_url: str | None = None,
    ) -> Listing:
        return _run(
            self._async.create_listing(
                title, slug, listing_type, price,
                description=description, side=side, tags=tags,
                input_schema=input_schema, output_schema=output_schema,
                auto_accept=auto_accept,
                delivery_callback_url=delivery_callback_url,
            )
        )

    def list_listings(self) -> list[Listing]:
        return _run(self._async.list_listings())

    def get_listing(self, listing_id: str) -> Listing:
        return _run(self._async.get_listing(listing_id))

    def update_listing(self, listing_id: str, **fields: Any) -> Listing:
        return _run(self._async.update_listing(listing_id, **fields))

    def deactivate_listing(self, listing_id: str) -> Listing:
        return _run(self._async.deactivate_listing(listing_id))

    # ── Marketplace ──────────────────────────────────────────────────

    def browse(
        self,
        *,
        listing_type: str | None = None,
        side: str | None = None,
        tags: str | None = None,
        min_price: float | None = None,
        max_price: float | None = None,
        search: str | None = None,
        offset: int = 0,
        limit: int = 50,
    ) -> Page:
        return _run(
            self._async.browse(
                listing_type=listing_type, side=side, tags=tags,
                min_price=min_price, max_price=max_price, search=search,
                offset=offset, limit=limit,
            )
        )

    def get_marketplace_listing(self, listing_id: str) -> MarketplaceListing:
        return _run(self._async.get_marketplace_listing(listing_id))

    # ── Trades ───────────────────────────────────────────────────────

    def propose_trade(
        self,
        listing_id: str,
        terms: str | None = None,
        *,
        metadata: dict[str, Any] | None = None,
        verification_url: str | None = None,
    ) -> Trade:
        return _run(
            self._async.propose_trade(
                listing_id, terms,
                metadata=metadata, verification_url=verification_url,
            )
        )

    def list_trades(
        self,
        *,
        role: str | None = None,
        status: str | None = None,
        offset: int = 0,
        limit: int = 50,
    ) -> Page:
        return _run(
            self._async.list_trades(
                role=role, status=status, offset=offset, limit=limit
            )
        )

    def get_trade(self, trade_id: str) -> Trade:
        return _run(self._async.get_trade(trade_id))

    def accept_trade(self, trade_id: str) -> Trade:
        return _run(self._async.accept_trade(trade_id))

    def reject_trade(self, trade_id: str, reason: str | None = None) -> Trade:
        return _run(self._async.reject_trade(trade_id, reason))

    def cancel_trade(self, trade_id: str, reason: str | None = None) -> Trade:
        return _run(self._async.cancel_trade(trade_id, reason))

    def deliver_trade(
        self,
        trade_id: str,
        *,
        payload: dict[str, Any] | None = None,
        endpoint_url: str | None = None,
        credentials: dict[str, Any] | None = None,
        transaction_proof: dict[str, Any] | None = None,
    ) -> Trade:
        return _run(
            self._async.deliver_trade(
                trade_id,
                payload=payload, endpoint_url=endpoint_url,
                credentials=credentials, transaction_proof=transaction_proof,
            )
        )

    def confirm_trade(self, trade_id: str) -> Trade:
        return _run(self._async.confirm_trade(trade_id))

    def dispute_trade(self, trade_id: str, reason: str | None = None) -> Trade:
        return _run(self._async.dispute_trade(trade_id, reason))

    # ── Reviews ──────────────────────────────────────────────────────

    def create_review(
        self, trade_id: str, rating: int, body: str | None = None
    ) -> Review:
        return _run(self._async.create_review(trade_id, rating, body))

    # ── Wallet ───────────────────────────────────────────────────────

    def get_wallet(self) -> Wallet:
        return _run(self._async.get_wallet())

    def get_transactions(self, *, offset: int = 0, limit: int = 50) -> Page:
        return _run(self._async.get_transactions(offset=offset, limit=limit))

    # ── Public stats ─────────────────────────────────────────────────

    def get_stats(self) -> PlatformStats:
        return _run(self._async.get_stats())

    # ── Fulfill ────────────────────────────────────────────────────

    def fulfill(
        self,
        need: str,
        *,
        budget: float | None = None,
        listing_type: str | None = None,
        tags: list[str] | None = None,
        auto_propose: bool = True,
        terms: str | None = None,
    ) -> dict[str, Any]:
        """Find best matching listing and auto-propose a trade."""
        return _run(
            self._async.fulfill(
                need, budget=budget, listing_type=listing_type,
                tags=tags, auto_propose=auto_propose, terms=terms,
            )
        )

    # ── Subscriptions ──────────────────────────────────────────────

    def subscribe(
        self,
        event_type: str,
        webhook_url: str,
        *,
        filters: dict[str, Any] | None = None,
        secret: str | None = None,
    ) -> dict[str, Any]:
        """Create a webhook subscription for an event type."""
        return _run(
            self._async.subscribe(
                event_type, webhook_url,
                filters=filters, secret=secret,
            )
        )

    def list_subscriptions(self) -> list[dict[str, Any]]:
        """List all active subscriptions."""
        return _run(self._async.list_subscriptions())

    def unsubscribe(self, subscription_id: str) -> None:
        """Delete a subscription."""
        return _run(self._async.unsubscribe(subscription_id))

    # ── Intelligence ──────────────────────────────────────────────

    def get_market_gaps(self, limit: int = 20) -> list[dict[str, Any]]:
        return _run(self._async.get_market_gaps(limit=limit))

    def get_trending(self, days: int = 7, limit: int = 10) -> dict[str, Any]:
        return _run(self._async.get_trending(days=days, limit=limit))

    def get_price_guide(
        self, listing_type: str | None = None, tags: str | None = None
    ) -> dict[str, Any]:
        return _run(self._async.get_price_guide(listing_type=listing_type, tags=tags))

    def get_opportunities(
        self, listing_type: str | None = None, limit: int = 20
    ) -> dict[str, Any]:
        return _run(self._async.get_opportunities(listing_type=listing_type, limit=limit))

    def get_my_analytics(self) -> dict[str, Any]:
        return _run(self._async.get_my_analytics())

    def get_badges(self) -> dict[str, Any]:
        return _run(self._async.get_badges())

    # ── Inbox ─────────────────────────────────────────────────────

    def get_inbox(
        self,
        *,
        unread: bool = False,
        event_type: str | None = None,
        offset: int = 0,
        limit: int = 50,
    ) -> dict[str, Any]:
        return _run(
            self._async.get_inbox(
                unread=unread, event_type=event_type,
                offset=offset, limit=limit,
            )
        )

    def ack_inbox(self, notification_ids: list[str]) -> dict[str, Any]:
        return _run(self._async.ack_inbox(notification_ids))

    def ack_all_inbox(self) -> dict[str, Any]:
        return _run(self._async.ack_all_inbox())

    # ── Platform info ─────────────────────────────────────────────

    def get_platform_info(self) -> dict[str, Any]:
        return _run(self._async.get_platform_info())

    # ── Pipelines ─────────────────────────────────────────────────

    def create_pipeline(
        self,
        name: str,
        slug: str,
        steps: list[dict[str, Any]],
        *,
        description: str | None = None,
    ) -> dict[str, Any]:
        return _run(
            self._async.create_pipeline(name, slug, steps, description=description)
        )

    def list_pipelines(self) -> list[dict[str, Any]]:
        return _run(self._async.list_pipelines())

    def get_pipeline(self, pipeline_id: str) -> dict[str, Any]:
        return _run(self._async.get_pipeline(pipeline_id))

    def execute_pipeline(
        self, pipeline_id: str, input_data: dict[str, Any] | None = None
    ) -> dict[str, Any]:
        return _run(self._async.execute_pipeline(pipeline_id, input_data))

    def get_pipeline_execution(self, execution_id: str) -> dict[str, Any]:
        return _run(self._async.get_pipeline_execution(execution_id))
