from enum import StrEnum
from typing import List, Literal, Unpack

import orjson
from openai.types.audio.transcription import Transcription
from openai.types.batch import Batch
from openai.types.chat.chat_completion import ChatCompletion
from openai.types.create_embedding_response import CreateEmbeddingResponse
from openai.types.file_object import FileObject
from pydantic import BaseModel, ConfigDict, Field

from .client import AsyncClient, ClientKwargs, HttpKwargs
from .common import patch_response_class


class CollectorBindingInfoItem(BaseModel):
  model_config = ConfigDict(extra='allow')

  login_method: str | None = None
  account_uuid: str | None = None
  area_code: str | None = None
  phone: str | None = None
  username: str | None = None
  name: str | None = None
  mobile: str | None = None
  avatar: str | None = None
  nickname: str | None = None
  headimgurl: str | None = None


class AsyncDispatcherClient(AsyncClient):
  def __init__(
    self,
    token: str | None = None,
    **kwargs: Unpack[ClientKwargs],
  ):
    if kwargs.get('transport') is None:
      kwargs.pop('transport', None)
    headers = dict(kwargs.pop('headers', None) or {})
    if token:
      headers['authorization'] = token
    kwargs['headers'] = headers
    super().__init__(**kwargs)
    self.payment = self.Payment(self)
    self.chatmdt = self.ChatMDT(self)
    self.mapbox = self.Mapbox(self)
    self.collector = self.Collector(self)
    self.agents = self.Agents(self)
    self.duckdb = self.DuckDB(self)
    self.imgbb = self.Imgbb(self)
    self.miaomiao = self.MiaoMiao(self)

  class DuckDB:
    def __init__(self, client: 'AsyncDispatcherClient') -> None:
      self.client = client
      self.query = self.Query(client)

    class Query:
      def __init__(self, client: 'AsyncDispatcherClient') -> None:
        self.client = client

      @patch_response_class()
      async def csv(
        self,
        sql: str,
        *,
        use_cache: bool = False,
        cache_ttl: int = 0,
        timeout: int = 60,
        **kwargs,
      ):
        return await self.client.post(
          '/duckdb',
          json={
            'type': 'csv',
            'sql': sql,
            'use_cache': use_cache,
            'cache_ttl': cache_ttl,
          },
          timeout=timeout,
          **kwargs,
        )

      @patch_response_class()
      async def json(
        self,
        sql: str,
        *,
        use_cache: bool = False,
        cache_ttl: int = 0,
        timeout: int = 60,
        **kwargs,
      ):
        return await self.client.post(
          '/duckdb',
          json={
            'type': 'json',
            'sql': sql,
            'use_cache': use_cache,
            'cache_ttl': cache_ttl,
          },
          timeout=timeout,
          **kwargs,
        )

      @patch_response_class()
      async def exec(
        self,
        sql: str,
        *,
        timeout: int = 60,
        **kwargs,
      ):
        return await self.client.post(
          '/duckdb',
          json={'type': 'exec', 'sql': sql},
          timeout=timeout,
          **kwargs,
        )

  class Agents:
    class GroupItem(BaseModel):
      uuid: str
      name: str
      description: str
      dimension: int | None = None
      index_type: str | None = None
      ops: str | None = None
      create_time: int | None = None
      update_time: int | None = None
      type: str | None = None
      status: str | None = None

    class UpdateResult(BaseModel):
      success: bool
      data: dict = Field(default_factory=dict)

    def __init__(self, client: 'AsyncDispatcherClient') -> None:
      self.client = client
      self.group = self.Group(client)
      self.openai = self.OpenAI(client)

    @patch_response_class()
    async def download(
      self,
      file_id: int,
      redirect: bool = False,
      **kwargs,
    ):
      params = kwargs.pop('params', {}) or {}
      params['redirect'] = redirect
      return await self.client.get(
        f'/agents/download/{file_id}',
        params=params,
        **kwargs,
      )

    @patch_response_class(rc_response=True)
    async def get_voice_config(
      self,
      agent_name: str,
      **kwargs,
    ):
      params = kwargs.pop('params', {}) or {}
      params['agent_name'] = agent_name
      params['with_agent'] = 'true'
      return await self.client.get('/miaomiao/agents', params=params, **kwargs)

    @patch_response_class(rc_response=True)
    async def get_tour_voice_config(self, tour_id: int, **kwargs):
      return await self.client.get(
        f'/agents/tour/{tour_id}/voice-config',
        **kwargs,
      )

    class OpenAI:
      def __init__(self, client: 'AsyncDispatcherClient') -> None:
        self.client = client

      @patch_response_class(model=ChatCompletion)
      async def chat(
        self,
        body: dict,
        timeout: int = 120,
        **kwargs,
      ) -> ChatCompletion:
        body['stream'] = False
        params = kwargs.pop('params', {}) or {}
        params['timeout'] = timeout
        return await self.client.post(
          '/agents/openai/chat/completions',
          json=body,
          params=params,
          timeout=timeout,
          **kwargs,
        )

      @patch_response_class(model=CreateEmbeddingResponse)
      async def embedding(
        self,
        model: str,
        input: str | list[str],
        encoding_format: str = 'float',
        truncate: bool = True,
        random: bool = False,
        timeout: int = 3,
        **kwargs,
      ):
        params = kwargs.pop('params', {}) or {}
        params['timeout'] = timeout
        body = {
          'model': model,
          'input': input,
          'encoding_format': encoding_format,
          'truncate': truncate,
        }
        if random:
          body['random'] = True
        return await self.client.post(
          '/agents/embeddings',
          timeout=timeout,
          params=params,
          json=body,
          **kwargs,
        )

      @patch_response_class(model=Transcription)
      async def transcribe(
        self,
        model: str,
        file: tuple[str, bytes],
        stream: bool = False,
        timeout: int = 600,
        config: dict | None = None,
        **kwargs,
      ) -> Transcription:
        params = kwargs.pop('params', {}) or {}
        params['timeout'] = timeout
        data = {'model': model, 'stream': stream}
        if config is not None:
          data['config'] = orjson.dumps(config).decode()
        return await self.client.post_or_stream(
          '/agents/audio/transcriptions',
          stream=stream,
          data=data,
          files={'file': file},
          params=params,
          timeout=timeout,
          **kwargs,
        )

      @patch_response_class(model=Batch)
      async def create_batch(
        self,
        input_file_id: str,
        endpoint: str,
        completion_window: Literal['24h'] = '24h',
        metadata: dict[str, str] | None = None,
        timeout: int = 60,
        **kwargs,
      ):
        params = kwargs.pop('params', {}) or {}
        params['timeout'] = timeout
        return await self.client.post(
          '/agents/batches',
          json={
            'input_file_id': input_file_id,
            'endpoint': endpoint,
            'completion_window': completion_window,
            'metadata': metadata or {},
          },
          params=params,
          timeout=timeout,
          **kwargs,
        )

      @patch_response_class(model=Batch)
      async def retrieve_batch(
        self, batch_id: str, timeout: int = 60, **kwargs
      ):
        params = kwargs.pop('params', {}) or {}
        params['timeout'] = timeout
        return await self.client.get(
          f'/agents/batches/{batch_id}',
          params=params,
          timeout=timeout,
          **kwargs,
        )

      @patch_response_class(model=FileObject)
      async def create_file(
        self,
        file: tuple[str, bytes],
        purpose: str = 'batch',
        **kwargs,
      ):
        return await self.client.post(
          '/agents/files',
          files={'file': file},
          data={'purpose': purpose},
          **kwargs,
        )

      @patch_response_class(model=FileObject)
      async def retrieve_file(self, file_id: str, timeout: int = 60, **kwargs):
        params = kwargs.pop('params', {}) or {}
        params['timeout'] = timeout
        return await self.client.get(
          f'/agents/files/{file_id}',
          params=params,
          timeout=timeout,
          **kwargs,
        )

      async def download_file_content(
        self,
        file_id: str,
        timeout: int = 60,
        **kwargs,
      ):
        params = kwargs.pop('params', {}) or {}
        params['timeout'] = timeout
        return await self.client.get(
          f'/agents/files/{file_id}/content',
          params=params,
          timeout=timeout,
          **kwargs,
        )

    class Group:
      class ListKnowledgeResponse(BaseModel):
        data: list[dict]
        cursor: str

      def __init__(self, client: 'AsyncDispatcherClient') -> None:
        self.client = client
        self.knowledge = self.Knowledge(client)

      @patch_response_class(model=None, rc_response=True)
      async def list(
        self,
        page_size: int | None = None,
        page_num: int | None = None,
        **kwargs,
      ):
        params = {}
        if page_size is not None:
          params['page_size'] = page_size
        if page_num is not None:
          params['page_num'] = page_num
        return await self.client.get(
          '/agents/knowledge/group',
          params=params,
          **kwargs,
        )

      @patch_response_class(model=None, rc_response=True)
      async def list_v2(
        self,
        *,
        page_size: int = 10,
        page_num: int = 0,
        q: str | None = None,
        status: List[str] | None = None,
        type: List[str] | None = None,
        sort_by: str = 'update_time',
        sort_order: str = 'desc',
        **kwargs,
      ):
        body = {
          'q': q,
          'status': status or ['ready', 'unready'],
          'type': type or [],
          'sort_by': sort_by,
          'sort_order': sort_order,
        }
        return await self.client.post(
          '/agents/knowledge/group/list',
          json=body,
          params={'page_size': page_size, 'page_num': page_num},
          **kwargs,
        )

      @patch_response_class(model=None, rc_response=True)
      async def get(self, group_id: str, **kwargs):
        return await self.client.get(
          f'/agents/knowledge/group/{group_id}',
          **kwargs,
        )

      @patch_response_class(model=None, rc_response=True)
      async def create(
        self,
        name: str,
        description: str = '',
        modality: List[str] | None = None,
        uuid: str | None = None,
        extra_schema: dict | None = None,
        type: str = 'naive',
        **kwargs,
      ):
        body = {
          'name': name,
          'description': description,
          'modality': modality or ['text'],
          'extra_schema': extra_schema or {},
          'type': type,
        }
        if uuid is not None:
          body['uuid'] = uuid
        return await self.client.post(
          '/agents/knowledge/group',
          json=body,
          **kwargs,
        )

      @patch_response_class(model=None, rc_response=True)
      async def update(
        self,
        group_id: str,
        *,
        ts: int,
        name: str | None = None,
        description: str | None = None,
        extra_schema: dict | None = None,
        **kwargs,
      ):
        body = {'ts': ts}
        if name is not None:
          body['name'] = name
        if description is not None:
          body['description'] = description
        if extra_schema is not None:
          body['extra_schema'] = extra_schema
        return await self.client.patch(
          f'/agents/knowledge/group/{group_id}',
          json=body,
          **kwargs,
        )

      @patch_response_class(model=None, rc_response=True)
      async def delete(
        self,
        group_id: str,
        ts: int,
        cascade: bool = False,
        **kwargs,
      ):
        return await self.client.delete(
          f'/agents/knowledge/group/{group_id}',
          params={'ts': ts, 'cascade': cascade},
          **kwargs,
        )

      class Knowledge:
        def __init__(self, client: 'AsyncDispatcherClient') -> None:
          self.client = client

        @patch_response_class(model=None, rc_response=True)
        async def listing(
          self,
          group_id: str,
          search_type: Literal[
            'chunk',
            'entity',
            'relation',
            'community',
            'document',
          ],
          page_size: int = 10,
          cursor: str | None = None,
          **kwargs,
        ):
          return await self.client.get(
            f'/agents/knowledge/group/{group_id}/knowledge',
            params={
              'page_size': page_size,
              'cursor': cursor,
              'search_type': search_type,
            },
            **kwargs,
          )

        @patch_response_class(model=None, rc_response=True)
        async def search(
          self,
          group_id: str,
          search: dict,
          format: Literal['text', 'json'] = 'json',
          **kwargs,
        ):
          return await self.client.post(
            f'/agents/knowledge/group/{group_id}/knowledge/search',
            json={'format': format, 'search': search},
            **kwargs,
          )

        @patch_response_class(model=None, rc_response=True)
        async def truncate(
          self,
          group_id: str,
          chunk: bool = False,
          entity: bool = False,
          relation: bool = False,
          community: bool = False,
          document: bool = False,
          **kwargs,
        ):
          return await self.client.post(
            f'/agents/knowledge/group/{group_id}/knowledge/truncate',
            json={
              'chunk': chunk,
              'entity': entity,
              'relation': relation,
              'community': community,
              'document': document,
            },
            **kwargs,
          )

        @patch_response_class(model=None, rc_response=True)
        async def embed(
          self,
          group_id: str,
          embed_all: bool = False,
          **kwargs,
        ):
          return await self.client.post(
            f'/agents/knowledge/group/{group_id}/knowledge/embed',
            json={'embed_all': embed_all},
            **kwargs,
          )

        @patch_response_class(model=None, rc_response=True)
        async def add(
          self,
          group_id: str,
          chunks: list[dict] | None = None,
          documents: list[dict] | None = None,
          action: Literal[
            'on_conflict_do_update',
            'on_conflict_do_nothing',
          ] = 'on_conflict_do_update',
          embed: bool = False,
          **kwargs,
        ):
          if not chunks and not documents:
            raise ValueError('added data is required')
          body = {'embed': embed, 'action': action}
          if chunks:
            body['chunks'] = chunks
          if documents:
            body['documents'] = documents
          return await self.client.post(
            f'/agents/knowledge/group/{group_id}/knowledge',
            json=body,
            **kwargs,
          )

  class Collector:
    def __init__(self, client: 'AsyncDispatcherClient') -> None:
      self.client = client
      self.account = self.Account(client)
      self.app = self.App(client)
      self.fixed_token = self.FixedToken(client)
      self.permission = self.Permission(client)
      self.role = self.Role(client)

    class Account:
      class AccountInfoResponse(BaseModel):
        main_login_method: str | None = None
        binding_info: list[CollectorBindingInfoItem] = Field(
          default_factory=list
        )

      def __init__(self, client: 'AsyncDispatcherClient') -> None:
        self.client = client

      @patch_response_class(model=AccountInfoResponse, rc_response=True)
      async def info(self, **kwargs):
        return await self.client.get('/collector/account/info', **kwargs)

    class App:
      class AppListResponse(BaseModel):
        items: list[dict] = Field(default_factory=list)
        total: int
        page: int
        page_size: int
        total_pages: int

      def __init__(self, client: 'AsyncDispatcherClient') -> None:
        self.client = client

      @patch_response_class(model=AppListResponse, rc_response=True)
      async def list(
        self,
        *,
        id: int | None = None,
        status: int | None = None,
        name: str | None = None,
        sort_by: str = 'create_time',
        sort_order: str = 'desc',
        page_num: int = 0,
        page_size: int = 10,
        **kwargs,
      ):
        params = {
          'sort_by': sort_by,
          'sort_order': sort_order,
          'page_num': page_num,
          'page_size': page_size,
        }
        if id is not None:
          params['id'] = id
        if status is not None:
          params['status'] = status
        if name is not None:
          params['name'] = name
        return await self.client.get('/collector/app', params=params, **kwargs)

      @patch_response_class(model=None, rc_response=True)
      async def create(
        self,
        *,
        name: str,
        description: str,
        status: int = 1,
        primary_login_method: str | None = None,
        primary_login_setting_id: str | None = None,
        **kwargs,
      ):
        return await self.client.post(
          '/collector/app',
          json={
            'name': name,
            'description': description,
            'status': status,
            'primary_login_method': primary_login_method,
            'primary_login_setting_id': primary_login_setting_id,
          },
          **kwargs,
        )

      @patch_response_class(model=None, rc_response=True)
      async def update(
        self,
        app_id: int,
        *,
        name: str | None = None,
        description: str | None = None,
        status: int | None = None,
        primary_login_method: str | None = None,
        primary_login_setting_id: str | None = None,
        **kwargs,
      ):
        body = {}
        if name is not None:
          body['name'] = name
        if description is not None:
          body['description'] = description
        if status is not None:
          body['status'] = status
        if primary_login_method is not None:
          body['primary_login_method'] = primary_login_method
        if primary_login_setting_id is not None:
          body['primary_login_setting_id'] = primary_login_setting_id
        return await self.client.patch(
          f'/collector/app/{app_id}',
          json=body,
          **kwargs,
        )

    class FixedToken:
      def __init__(self, client: 'AsyncDispatcherClient') -> None:
        self.client = client

      @patch_response_class(model=None, rc_response=True)
      async def modify(
        self,
        token_id: int,
        *,
        description: str | None = None,
        ready: bool | None = None,
        **kwargs,
      ):
        body = {}
        if description is not None:
          body['description'] = description
        if ready is not None:
          body['ready'] = ready
        return await self.client.patch(
          f'/collector/account/authenticate/token/{token_id}',
          json=body,
          **kwargs,
        )

    class Permission:
      def __init__(self, client: 'AsyncDispatcherClient') -> None:
        self.client = client

      @patch_response_class(model=None, rc_response=True)
      async def read(
        self,
        *,
        ids: list[int] | None = None,
        names: list[str] | None = None,
        patterns: list[str] | None = None,
        with_extra: bool = False,
        page_size: int = 100,
        page_num: int = 0,
        **kwargs,
      ):
        body = {
          'with_extra': with_extra,
          'page_size': page_size,
          'page_num': page_num,
        }
        if ids:
          body['ids'] = ids
        if names:
          body['names'] = names
        if patterns:
          body['patterns'] = patterns
        return await self.client.post(
          '/permission/read',
          json=body,
          **kwargs,
        )

      @patch_response_class(model=None, rc_response=True)
      async def granted(
        self,
        *,
        target_accounts: list[str] | None = None,
        patterns: list[str] | None = None,
        **kwargs,
      ):
        body = {}
        if target_accounts:
          body['target_accounts'] = target_accounts
        if patterns:
          body['patterns'] = patterns
        return await self.client.post(
          '/permission/granted',
          json=body,
          **kwargs,
        )

      @patch_response_class(model=None, rc_response=True)
      async def create(
        self,
        permissions: dict | list[dict],
        **kwargs,
      ):
        return await self.client.post(
          '/permission/create',
          json=permissions,
          **kwargs,
        )

      @patch_response_class(model=None, rc_response=True)
      async def grant(
        self,
        bindings: dict | list[dict],
        **kwargs,
      ):
        return await self.client.post(
          '/permission/grant',
          json=bindings,
          **kwargs,
        )

    class Role:
      def __init__(self, client: 'AsyncDispatcherClient') -> None:
        self.client = client

      @patch_response_class(model=None, rc_response=True)
      async def relation(
        self,
        relations: list[dict],
        *,
        action: Literal['grant', 'revoke'] = 'grant',
        **kwargs,
      ):
        return await self.client.post(
          '/role/relation',
          json={'action': action, 'relations': relations},
          **kwargs,
        )

      @patch_response_class(model=None, rc_response=True)
      async def get(
        self,
        *,
        nickname: str,
        **kwargs,
      ):
        return await self.client.get(
          '/role',
          params={'nickname': nickname},
          **kwargs,
        )

      @patch_response_class(model=None, rc_response=True)
      async def create(
        self,
        *,
        nickname: str,
        **kwargs,
      ):
        return await self.client.post(
          '/role',
          json={'nickname': nickname},
          **kwargs,
        )

  class Mapbox:
    def __init__(self, client: 'AsyncDispatcherClient') -> None:
      self.client = client

    class Profile(StrEnum):
      driving_traffic = 'driving-traffic'
      driving = 'driving'
      walking = 'walking'
      cycling = 'cycling'
      transport = 'transport'

  class ChatMDT:
    def __init__(self, client: 'AsyncDispatcherClient') -> None:
      self.client = client
      self.anything = self.Anything(client)

    class Anything:
      def __init__(self, client: 'AsyncDispatcherClient') -> None:
        self.client = client

      @patch_response_class(model=None, rc_response=True)
      async def retrieve(
        self,
        anything_uuid: str,
        body: dict | BaseModel,
        **kwargs: Unpack[HttpKwargs],
      ):
        if not isinstance(body, dict):
          body = body.model_dump(exclude_unset=True)
        return await self.client.post(
          f'/chatmdt/anything/{anything_uuid}/retrieve',
          json=body,
          **kwargs,
        )

  class Payment:
    def __init__(self, client: 'AsyncDispatcherClient') -> None:
      self.client = client
      self.voucher = self.Voucher(client)

    class Voucher:
      def __init__(self, client: 'AsyncDispatcherClient') -> None:
        self.client = client

      @patch_response_class(model=None, rc_response=True)
      async def create_code(
        self,
        setting_id: str,
        n: int = 1,
        args: dict | None = None,
        valid_till: int | None = None,
        code: str | None = None,
      ):
        return await self.client.post(
          f'/payment/voucher/{setting_id}/create',
          json={
            'args': args or {},
            'n': n,
            'valid_till': valid_till,
            'code': code,
          },
        )

      @patch_response_class(model=None, rc_response=True)
      async def create_setting(
        self,
        name: str,
        input_args: dict | None = None,
        system_args: dict | None = None,
      ):
        return await self.client.post(
          '/payment/voucher/setting',
          json={
            'name': name,
            'input_args': input_args or {},
            'system_args': system_args or {},
          },
        )

      @patch_response_class(model=None, rc_response=True)
      async def redeem_topup_share_code(self, topup_share_code: str):
        return await self.client.post(
          f'/payment/voucher/redeem/{topup_share_code}'
        )

      async def get_voucher_code_setting(self, voucher_code: str):
        res = await self.client.get(f'/payment/voucher/setting/{voucher_code}')
        if res.status_code == 404:
          return None
        return res

      @patch_response_class(model=None, rc_response=True)
      async def validate_voucher_code(self, voucher_code: str):
        return await self.client.get(
          f'/payment/voucher/validate/{voucher_code}'
        )

  class MiaoMiao:
    def __init__(self, client: 'AsyncDispatcherClient') -> None:
      self.client = client
      self.account = self.Account(client)
      self.agent = self.Agent(client)
      self.version = self.Version(client)

    class Account:
      def __init__(self, client: 'AsyncDispatcherClient') -> None:
        self.client = client

      @patch_response_class(model=None, rc_response=True)
      async def info(self, **kwargs):
        return await self.client.get('/miaomiao/account/info', **kwargs)

      @patch_response_class(model=None, rc_response=True)
      async def update(
        self,
        *,
        name: str | None = None,
        avatar: str | None = None,
        gender: int | None = None,
        agreement: bool | None = None,
        agent_name: str | None = None,
        **kwargs,
      ):
        body = {}
        for key, value in (
          ('name', name),
          ('avatar', avatar),
          ('gender', gender),
          ('agreement', agreement),
          ('agent_name', agent_name),
        ):
          if value is not None:
            body[key] = value
        return await self.client.post(
          '/miaomiao/account/info',
          json=body,
          **kwargs,
        )

    class Agent:
      def __init__(self, client: 'AsyncDispatcherClient') -> None:
        self.client = client

      @patch_response_class(model=None, rc_response=True)
      async def list(
        self,
        *,
        agent_id: int | None = None,
        agent_name: str | None = None,
        account_id: int | None = None,
        with_agent: bool = False,
        page_num: int = 0,
        page_size: int = 10,
        **kwargs,
      ):
        params = {
          'with_agent': with_agent,
          'page_num': page_num,
          'page_size': page_size,
        }
        if agent_id is not None:
          params['agent_id'] = agent_id
        if agent_name is not None:
          params['agent_name'] = agent_name
        if account_id is not None:
          params['account_id'] = account_id
        return await self.client.get(
          '/miaomiao/agents', params=params, **kwargs
        )

      @patch_response_class(model=None, rc_response=True)
      async def create(
        self,
        *,
        name: str,
        app_id: int,
        account_id: int | None = None,
        agent_name: str | None = None,
        extra: dict | None = None,
        geo_fence: dict | None = None,
        **kwargs,
      ):
        body = {
          'name': name,
          'app_id': app_id,
          'account_id': account_id,
          'agent_name': agent_name,
          'extra': extra or {},
          'geo_fence': geo_fence,
        }
        return await self.client.post('/miaomiao/agents', json=body, **kwargs)

      @patch_response_class(model=None, rc_response=True)
      async def update(
        self,
        agent_id: int,
        *,
        ts: int,
        name: str | None = None,
        avatar: str | None = None,
        agent_name: str | None = None,
        extra: dict | None = None,
        account_id: int | None = None,
        geo_fence: dict | None = None,
        **kwargs,
      ):
        body = {'ts': ts}
        for key, value in (
          ('name', name),
          ('avatar', avatar),
          ('agent_name', agent_name),
          ('extra', extra),
          ('account_id', account_id),
          ('geo_fence', geo_fence),
        ):
          if value is not None:
            body[key] = value
        return await self.client.patch(
          f'/miaomiao/agents/{agent_id}',
          json=body,
          **kwargs,
        )

      @patch_response_class(model=None, rc_response=True)
      async def delete(self, agent_id: int, ts: int, **kwargs):
        return await self.client.delete(
          f'/miaomiao/agents/{agent_id}',
          params={'ts': ts},
          **kwargs,
        )

    class Version:
      def __init__(self, client: 'AsyncDispatcherClient') -> None:
        self.client = client

      @patch_response_class(model=None, rc_response=True)
      async def list(
        self,
        *,
        platform: str | None = None,
        id: int | None = None,
        page_num: int = 0,
        page_size: int = 10,
        **kwargs,
      ):
        params = {'page_num': page_num, 'page_size': page_size}
        if platform is not None:
          params['platform'] = platform
        if id is not None:
          params['id'] = id
        return await self.client.get(
          '/miaomiao/version', params=params, **kwargs
        )

      @patch_response_class(model=None, rc_response=True)
      async def create(
        self,
        *,
        platform: str,
        latest_version: str,
        minimum_supported_version: str,
        release_notes: str,
        download_url: str,
        **kwargs,
      ):
        return await self.client.post(
          '/miaomiao/version',
          json={
            'platform': platform,
            'latest_version': latest_version,
            'minimum_supported_version': minimum_supported_version,
            'release_notes': release_notes,
            'download_url': download_url,
          },
          **kwargs,
        )

      @patch_response_class(model=None, rc_response=True)
      async def update(
        self,
        version_id: int,
        *,
        ts: int,
        latest_version: str | None = None,
        minimum_supported_version: str | None = None,
        release_notes: str | None = None,
        download_url: str | None = None,
        **kwargs,
      ):
        body = {'ts': ts}
        for key, value in (
          ('latest_version', latest_version),
          ('minimum_supported_version', minimum_supported_version),
          ('release_notes', release_notes),
          ('download_url', download_url),
        ):
          if value is not None:
            body[key] = value
        return await self.client.patch(
          f'/miaomiao/version/{version_id}',
          json=body,
          **kwargs,
        )

      @patch_response_class(model=None, rc_response=True)
      async def delete(self, version_id: int, ts: int, **kwargs):
        return await self.client.delete(
          f'/miaomiao/version/{version_id}',
          params={'ts': ts},
          **kwargs,
        )

  class Imgbb:
    def __init__(self, client: 'AsyncDispatcherClient') -> None:
      self.client = client

    @patch_response_class(model=None)
    async def upload(
      self,
      image: bytes,
      name: str | None = None,
      expiration: int | None = None,
      timeout: int = 30,
      **kwargs,
    ):
      files = {'image': ('image', image)}
      data = {}
      if name:
        data['name'] = name
      if expiration:
        data['expiration'] = str(expiration)
      return await self.client.post(
        '/dispatch/api.imgbb.com/1/upload',
        files=files,
        data=data,
        timeout=timeout,
        **kwargs,
      )
