import { BadRequestException, Controller, UseFilters } from '@nestjs/common';
import { GrpcMethod } from '@nestjs/microservices';
import { GrpcExceptionFilter } from '../infra/grpc-exception.filter';
import { AdminService } from './admin.service';
import { AuthService } from './auth.service';
import { LoginCommand, RegisterCommand, VerifyPinCommand } from './dto';
import { PinService } from './pin.service';
import { SessionService } from './session.service';
import { RbacService } from './rbac.service';
import { SecurityService } from './security.service';
import { SettingsService } from './settings.service';
import { UserStatus } from '../generated/prisma/client';
import { ProfileService } from './profile.service';
import { DocumentsService } from './documents.service';
import { AddressesService } from './addresses.service';
import { OAuthCoreService } from './oauth-core.service';
import { OtpService } from './otp.service';
import { AdvancedAuthService } from './advanced-auth.service';
import { FamilyService } from './family.service';
import { MediaService } from './media.service';
import { NotificationsService } from './notifications.service';
import { ChatService } from './chat.service';
import { PresenceService } from './presence.service';
import { TotpService } from './totp.service';
import { MessagingService } from '../infra/messaging.service';
import { BotFatherService } from './bot/bot-father.service';
import { BotApiService } from './bot/bot-api.service';
import { BotInboundService } from './bot/bot-inbound.service';

@Controller()
@UseFilters(new GrpcExceptionFilter())
export class AuthGrpcController {
  constructor(
    private readonly auth: AuthService,
    private readonly pin: PinService,
    private readonly session: SessionService,
    private readonly admin: AdminService,
    private readonly rbac: RbacService,
    private readonly security: SecurityService,
    private readonly settings: SettingsService,
    private readonly profile: ProfileService,
    private readonly documents: DocumentsService,
    private readonly addresses: AddressesService,
    private readonly oauthCore: OAuthCoreService,
    private readonly otp: OtpService,
    private readonly advancedAuth: AdvancedAuthService,
    private readonly family: FamilyService,
    private readonly media: MediaService,
    private readonly notifications: NotificationsService,
    private readonly chat: ChatService,
    private readonly presence: PresenceService,
    private readonly messaging: MessagingService,
    private readonly totp: TotpService,
    private readonly botFather: BotFatherService,
    private readonly botApi: BotApiService,
    private readonly botInbound: BotInboundService
  ) {}

  @GrpcMethod('AuthService', 'Register')
  register(command: RegisterCommand) {
    return this.auth.register(command);
  }

  @GrpcMethod('AuthService', 'Login')
  login(command: LoginCommand) {
    return this.auth.login(command);
  }

  @GrpcMethod('AuthService', 'Identify')
  identify(command: { login: string }) {
    return this.auth.identify(command.login);
  }

  @GrpcMethod('AuthService', 'SendOtp')
  sendPasswordlessOtp(command: { recipient: string; channel?: string; ipAddress?: string }) {
    return this.auth.sendOtp(command.recipient, command.channel, command.ipAddress);
  }

  @GrpcMethod('AuthService', 'VerifyOtp')
  verifyPasswordlessOtp(command: LoginCommand & { recipient: string; code: string }) {
    return this.auth.verifyOtp(command);
  }

  @GrpcMethod('AuthService', 'LoginWithPassword')
  loginWithPassword(command: LoginCommand & { tempAuthToken?: string }) {
    return this.auth.loginWithPassword(command);
  }

  @GrpcMethod('AuthService', 'LoginWithLdap')
  loginWithLdap(command: {
    username: string;
    password: string;
    fingerprint: string;
    deviceName?: string;
    deviceType?: string;
    ipAddress?: string;
    userAgent?: string;
  }) {
    return this.auth.loginWithLdap(command);
  }

  @GrpcMethod('AuthService', 'VerifyTotpLogin')
  verifyTotpLogin(command: { totpChallengeToken: string; code: string }) {
    return this.auth.verifyTotpLogin(command.totpChallengeToken, command.code);
  }

  @GrpcMethod('AuthService', 'BeginTotpLogin')
  beginTotpLogin(command: {
    recipient: string;
    fingerprint: string;
    deviceName?: string;
    deviceType?: string;
    ipAddress?: string;
    userAgent?: string;
  }) {
    return this.auth.beginTotpLogin(command);
  }

  @GrpcMethod('AuthService', 'VerifyPin')
  verifyPin(command: VerifyPinCommand) {
    return this.pin.verifyPin(command.sessionId, command.pin);
  }

  @GrpcMethod('AuthService', 'GetMe')
  getMe(command: { userId: string }) {
    return this.auth.getMe(command.userId);
  }

  @GrpcMethod('AuthService', 'RefreshSession')
  refreshSession(command: { refreshToken: string; sessionId?: string }) {
    return this.session.refreshSession(command.refreshToken, command.sessionId);
  }

  @GrpcMethod('AuthService', 'ValidateSession')
  validateSession(command: { userId: string; sessionId: string; touchActivity?: boolean }) {
    return this.session.validateSession(command.userId, command.sessionId, command.touchActivity ?? false);
  }

  @GrpcMethod('AdminService', 'ListUsers')
  async listUsers(command: { search?: string }) {
    const users = await this.admin.listUsers(command.search);
    return { users };
  }

  @GrpcMethod('AdminService', 'ListBotAccounts')
  async listBotAccounts(command: { search?: string }) {
    const users = await this.admin.listBotAccounts(command.search);
    return { users };
  }

  @GrpcMethod('AdminService', 'SuspendUser')
  suspendUser(command: { userId: string }) {
    return this.admin.suspendUser(command.userId);
  }

  @GrpcMethod('AdminService', 'UpdateUserProfile')
  updateUserProfile(command: { userId: string; displayName?: string; email?: string; phone?: string; backupEmail?: string; backupPhone?: string; status?: UserStatus }) {
    const { userId, ...data } = command;
    return this.admin.updateUserProfile(userId, data);
  }

  @GrpcMethod('AdminService', 'ResetPassword')
  resetPassword(command: { userId: string; newPassword: string }) {
    return this.admin.resetPassword(command.userId, command.newPassword);
  }

  @GrpcMethod('AdminService', 'SetSuperAdmin')
  setSuperAdmin(command: { actorUserId: string; userId: string; isSuperAdmin: boolean }) {
    return this.admin.setSuperAdmin(command.actorUserId, command.userId, command.isSuperAdmin);
  }

  @GrpcMethod('AdminService', 'SetUserVerification')
  setUserVerification(command: { actorUserId: string; userId: string; isVerified: boolean; verificationIcon?: string }) {
    return this.admin.setUserVerification(command.actorUserId, command.userId, {
      isVerified: command.isVerified,
      verificationIcon: command.verificationIcon
    });
  }

  @GrpcMethod('AdminService', 'ListVerificationIcons')
  listVerificationIcons() {
    return this.admin.listVerificationIcons();
  }

  @GrpcMethod('RbacService', 'ListRoles')
  async listRoles() {
    const roles = await this.rbac.listRoles();
    return {
      roles: roles.map((role) => ({
        id: role.id,
        slug: role.slug,
        name: role.name,
        description: role.description ?? undefined,
        isSystem: role.isSystem,
        isDefault: role.isDefault,
        permissions: role.permissions.map((link) => ({
          id: link.permission.id,
          slug: link.permission.slug,
          name: link.permission.name,
          description: link.permission.description ?? undefined
        }))
      }))
    };
  }

  @GrpcMethod('RbacService', 'ListOAuthClients')
  async listOAuthClients(command: { actorUserId: string }) {
    const clients = await this.rbac.listOAuthClients(command.actorUserId);
    return {
      clients: clients.map((client) => ({
        id: client.id,
        name: client.name,
        clientId: client.clientId,
        redirectUris: client.redirectUris,
        scopes: client.scopes,
        isActive: client.isActive,
        type: client.type,
        createdById: client.createdById ?? undefined,
        createdByDisplayName: client.createdByDisplayName ?? undefined,
        canManage: client.canManage
      }))
    };
  }

  @GrpcMethod('RbacService', 'ListPermissions')
  async listPermissions() {
    const permissions = await this.rbac.listPermissions();
    return {
      permissions: permissions.map((permission) => ({
        id: permission.id,
        slug: permission.slug,
        name: permission.name,
        description: permission.description ?? undefined
      }))
    };
  }

  @GrpcMethod('RbacService', 'ListOAuthScopes')
  async listOAuthScopes() {
    const scopes = await this.rbac.listOAuthScopes();
    return {
      scopes: scopes.map((scope) => ({
        id: scope.id,
        slug: scope.slug,
        name: scope.name,
        description: scope.description ?? undefined
      }))
    };
  }

  @GrpcMethod('RbacService', 'CreateRole')
  async createRole(command: { actorUserId: string; slug: string; name: string; description?: string; permissionSlugs?: string[] }) {
    const role = await this.rbac.createRole(command.actorUserId, {
      slug: command.slug,
      name: command.name,
      description: command.description,
      permissionSlugs: command.permissionSlugs
    });
    return {
      id: role.id,
      slug: role.slug,
      name: role.name,
      description: role.description ?? undefined,
      isSystem: role.isSystem,
      isDefault: role.isDefault,
      permissions: role.permissions.map((link) => ({
        id: link.permission.id,
        slug: link.permission.slug,
        name: link.permission.name,
        description: link.permission.description ?? undefined
      }))
    };
  }

  @GrpcMethod('RbacService', 'UpdateRole')
  async updateRole(command: {
    actorUserId: string;
    roleSlug: string;
    name?: string;
    description?: string;
    permissionSlugs?: string[];
  }) {
    const role = await this.rbac.updateRole(command.actorUserId, command.roleSlug, {
      name: command.name,
      description: command.description,
      permissionSlugs: command.permissionSlugs
    });
    return {
      id: role.id,
      slug: role.slug,
      name: role.name,
      description: role.description ?? undefined,
      isSystem: role.isSystem,
      isDefault: role.isDefault,
      permissions: role.permissions.map((link) => ({
        id: link.permission.id,
        slug: link.permission.slug,
        name: link.permission.name,
        description: link.permission.description ?? undefined
      }))
    };
  }

  @GrpcMethod('RbacService', 'DeleteRole')
  async deleteRole(command: { actorUserId: string; roleSlug: string }) {
    await this.rbac.deleteRole(command.actorUserId, command.roleSlug);
    return {};
  }

  @GrpcMethod('RbacService', 'AssignUserRole')
  async assignUserRole(command: { actorUserId: string; userId: string; roleSlug: string }) {
    const roles = await this.rbac.assignRole(command.actorUserId, command.userId, command.roleSlug);
    return { userId: command.userId, roles };
  }

  @GrpcMethod('RbacService', 'RemoveUserRole')
  async removeUserRole(command: { actorUserId: string; userId: string; roleSlug: string }) {
    await this.rbac.removeRole(command.actorUserId, command.userId, command.roleSlug);
    return {};
  }

  @GrpcMethod('RbacService', 'AssignUserPermission')
  async assignUserPermission(command: { actorUserId: string; userId: string; permissionSlug: string }) {
    const permissions = await this.rbac.assignUserPermission(command.actorUserId, command.userId, command.permissionSlug);
    return { userId: command.userId, permissions };
  }

  @GrpcMethod('RbacService', 'RemoveUserPermission')
  async removeUserPermission(command: { actorUserId: string; userId: string; permissionSlug: string }) {
    const permissions = await this.rbac.removeUserPermission(command.actorUserId, command.userId, command.permissionSlug);
    return { userId: command.userId, permissions };
  }

  @GrpcMethod('RbacService', 'CreateOAuthClient')
  async createOAuthClient(command: { actorUserId: string; name: string; redirectUris: string[]; scopes: string[]; type?: string }) {
    const { client, clientSecret } = await this.rbac.createOAuthClient(command.actorUserId, {
      name: command.name,
      redirectUris: command.redirectUris,
      scopes: command.scopes,
      type: command.type as never
    });
    return {
      id: client.id,
      name: client.name,
      clientId: client.clientId,
      redirectUris: client.redirectUris,
      scopes: client.scopes.map((link) => link.scope.slug),
      isActive: client.isActive,
      type: client.type,
      clientSecret: clientSecret ?? undefined
    };
  }

  @GrpcMethod('RbacService', 'UpdateOAuthClient')
  async updateOAuthClient(command: {
    actorUserId: string;
    clientId: string;
    name?: string;
    redirectUris?: string[];
    scopes?: string[];
    isActive?: boolean;
  }) {
    const { client } = await this.rbac.updateOAuthClient(command.actorUserId, command.clientId, {
      name: command.name,
      redirectUris: command.redirectUris,
      scopes: command.scopes,
      isActive: command.isActive
    });
    return {
      id: client.id,
      name: client.name,
      clientId: client.clientId,
      redirectUris: client.redirectUris,
      scopes: client.scopes.map((link) => link.scope.slug),
      isActive: client.isActive,
      type: client.type
    };
  }

  @GrpcMethod('RbacService', 'RotateOAuthSecret')
  async rotateOAuthSecret(command: { actorUserId: string; clientId: string }) {
    return this.rbac.rotateOAuthSecret(command.actorUserId, command.clientId);
  }

  @GrpcMethod('RbacService', 'DeleteOAuthClient')
  async deleteOAuthClient(command: { actorUserId: string; clientId: string }) {
    return this.rbac.deleteOAuthClient(command.actorUserId, command.clientId);
  }

  @GrpcMethod('SecurityService', 'ListActiveDevices')
  async listActiveDevices(command: { userId: string; exceptSessionId?: string }) {
    const devices = await this.security.listActiveDevices(command.userId, command.exceptSessionId);
    return {
      devices: devices.map((device) => ({
        id: device.id,
        name: device.name,
        type: device.type,
        lastIp: device.lastIp,
        lastSeenAt: device.lastSeenAt.toISOString(),
        trusted: device.trusted
      }))
    };
  }

  @GrpcMethod('SecurityService', 'ListSignInHistory')
  async listSignInHistory(command: { userId: string }) {
    const events = await this.security.listSignInHistory(command.userId);
    return {
      events: events.map((event) => ({
        id: event.id,
        success: event.success,
        reason: event.reason,
        ipAddress: event.ipAddress,
        userAgent: event.userAgent,
        createdAt: event.createdAt.toISOString()
      }))
    };
  }

  @GrpcMethod('SecurityService', 'ListActiveSessions')
  async listActiveSessions(command: { userId: string }) {
    const sessions = await this.security.listActiveSessions(command.userId);
    return {
      sessions: sessions.map((session) => this.toSessionResponse(session))
    };
  }

  @GrpcMethod('SecurityService', 'LockSession')
  async lockSession(command: { userId: string; sessionId: string }) {
    return this.toSessionResponse(await this.security.lockSession(command.userId, command.sessionId));
  }

  @GrpcMethod('SecurityService', 'RevokeSession')
  async revokeSession(command: { userId: string; sessionId: string }) {
    return this.toSessionResponse(await this.security.revokeSession(command.userId, command.sessionId));
  }

  @GrpcMethod('SecurityService', 'RevokeDevice')
  async revokeDevice(command: { userId: string; deviceId: string }) {
    const result = await this.security.revokeDevice(command.userId, command.deviceId);
    return { count: result.count };
  }

  @GrpcMethod('SecurityService', 'RevokeAllSessions')
  async revokeAllSessions(command: { userId: string; exceptSessionId?: string }) {
    const result = await this.security.revokeAllSessions(command.userId, command.exceptSessionId);
    return { count: result.count };
  }

  @GrpcMethod('SecurityService', 'GetTotpStatus')
  getTotpStatus(command: { userId: string }) {
    return this.totp.getStatus(command.userId);
  }

  @GrpcMethod('SecurityService', 'SetupTotp')
  setupTotp(command: { userId: string }) {
    return this.totp.setup(command.userId);
  }

  @GrpcMethod('SecurityService', 'EnableTotp')
  enableTotp(command: { userId: string; code: string }) {
    return this.totp.enable(command.userId, command.code);
  }

  @GrpcMethod('SecurityService', 'DisableTotp')
  disableTotp(command: { userId: string; code: string }) {
    return this.totp.disable(command.userId, command.code);
  }

  @GrpcMethod('SecurityService', 'AdminDisableTotp')
  adminDisableTotp(command: { actorUserId: string; userId: string }) {
    return this.totp.adminDisable(command.actorUserId, command.userId);
  }

  @GrpcMethod('SecurityService', 'SetupPin')
  setupPin(command: { userId: string; pin: string }) {
    return this.pin.setupPin(command.userId, command.pin);
  }

  @GrpcMethod('SecurityService', 'UpdatePin')
  updatePin(command: { userId: string; pin: string }) {
    return this.pin.updatePin(command.userId, command.pin);
  }

  @GrpcMethod('SecurityService', 'RequestPinDeletion')
  requestPinDeletion(command: { userId: string; pin: string }) {
    return this.pin.requestPinDeletion(command.userId, command.pin);
  }

  @GrpcMethod('SecurityService', 'CancelPinDeletion')
  cancelPinDeletion(command: { userId: string }) {
    return this.pin.cancelPinDeletion(command.userId);
  }

  @GrpcMethod('SecurityService', 'VerifyPinCode')
  verifyPinCode(command: { sessionId: string; pin: string }) {
    return this.pin.verifyPin(command.sessionId, command.pin);
  }

  @GrpcMethod('SecurityService', 'ConnectLinkedAccount')
  async connectLinkedAccount(command: { userId: string; providerName: string; providerId: string; email?: string; displayName?: string; avatarUrl?: string }) {
    return this.toLinkedAccountResponse(await this.security.connectSocialAccount(command.userId, command));
  }

  @GrpcMethod('SecurityService', 'DisconnectLinkedAccount')
  disconnectLinkedAccount(command: { userId: string; providerName: string }) {
    return this.security.disconnectSocialAccount(command.userId, command.providerName);
  }

  @GrpcMethod('SecurityService', 'ListLinkedAccounts')
  async listLinkedAccounts(command: { userId: string }) {
    const accounts = await this.security.listLinkedAccounts(command.userId);
    return { accounts: accounts.map((account) => this.toLinkedAccountResponse(account)) };
  }

  @GrpcMethod('SettingsService', 'ListSettings')
  async listSettings() {
    const settings = await this.settings.list();
    return {
      settings: settings.map((setting) => ({
        id: setting.id,
        key: setting.key,
        value: setting.value,
        description: setting.description ?? undefined,
        isSecret: setting.isSecret
      }))
    };
  }

  @GrpcMethod('SettingsService', 'ListPublicSettings')
  async listPublicSettings() {
    const settings = await this.settings.listPublic();
    return {
      settings: settings.map((setting) => ({
        key: setting.key,
        value: setting.value
      }))
    };
  }

  @GrpcMethod('SettingsService', 'GetSetting')
  getSetting(command: { key: string }) {
    return this.settings.get(command.key);
  }

  @GrpcMethod('SettingsService', 'UpsertSetting')
  upsertSetting(command: { key: string; value: string; description?: string; isSecret?: boolean }) {
    return this.settings.upsert(command);
  }

  @GrpcMethod('SettingsService', 'DeleteSetting')
  deleteSetting(command: { key: string }) {
    return this.settings.delete(command.key);
  }

  @GrpcMethod('SettingsService', 'ListSocialProviders')
  async listSocialProviders() {
    const providers = await this.settings.listSocialProviders();
    return { providers: providers.map((provider) => this.toSocialProviderResponse(provider)) };
  }

  @GrpcMethod('SettingsService', 'UpsertSocialProvider')
  async upsertSocialProvider(command: { providerName: string; clientId: string; clientSecret?: string; isEnabled: boolean }) {
    return this.toSocialProviderResponse(await this.settings.upsertSocialProvider(command));
  }

  @GrpcMethod('SettingsService', 'DeleteSocialProvider')
  deleteSocialProvider(command: { providerName: string }) {
    return this.settings.deleteSocialProvider(command.providerName);
  }

  @GrpcMethod('SettingsService', 'TestMessagingDelivery')
  async testMessagingDelivery(command: { channel: string; target: string }) {
    const channel = command.channel === 'sms' ? 'sms' : 'email';
    if (!command.target?.trim()) {
      throw new BadRequestException('Укажите email или номер телефона для тестовой отправки');
    }
    return this.messaging.sendTestDelivery(channel, command.target.trim());
  }

  @GrpcMethod('ProfileService', 'GetProfile')
  getProfile(command: { userId: string }) {
    return this.profile.getProfile(command.userId);
  }

  @GrpcMethod('ProfileService', 'UpdateAvatar')
  updateAvatar(command: { userId: string; avatarUrl?: string; avatarStorageKey?: string }) {
    return this.profile.updateAvatar(command.userId, command.avatarUrl, command.avatarStorageKey);
  }

  @GrpcMethod('ProfileService', 'UpdateProfile')
  updateProfile(command: { userId: string; firstName?: string; lastName?: string; bio?: string; age?: number; gender?: string; birthDate?: string }) {
    return this.profile.updateProfile(command);
  }

  @GrpcMethod('ProfileService', 'UpdateContacts')
  updateContacts(command: { userId: string; email?: string; phone?: string; backupEmail?: string; backupPhone?: string }) {
    return this.profile.updateContacts(command);
  }

  @GrpcMethod('ProfileService', 'SetPassword')
  setPassword(command: { userId: string; password: string }) {
    return this.profile.setPassword(command.userId, command.password);
  }

  @GrpcMethod('ProfileService', 'SendPasswordVerificationOtp')
  sendPasswordVerificationOtp(command: { userId: string; channel: string }) {
    return this.profile.sendPasswordVerificationOtp(command.userId, command.channel as 'sms' | 'email');
  }

  @GrpcMethod('ProfileService', 'ChangePassword')
  changePassword(command: {
    userId: string;
    newPassword: string;
    currentPassword?: string;
    otpCode?: string;
    otpChannel?: string;
    totpCode?: string;
  }) {
    return this.profile.changePassword(command.userId, command.newPassword, command);
  }

  @GrpcMethod('ProfileService', 'RemovePassword')
  removePassword(command: {
    userId: string;
    currentPassword?: string;
    otpCode?: string;
    otpChannel?: string;
    totpCode?: string;
  }) {
    return this.profile.removePassword(command.userId, command);
  }

  @GrpcMethod('ProfileService', 'SoftDeleteProfile')
  softDeleteProfile(command: { userId: string }) {
    return this.profile.softDeleteProfile(command.userId);
  }

  @GrpcMethod('ProfileService', 'RequestAccountDeletion')
  requestAccountDeletion(command: { userId: string }) {
    return this.profile.requestAccountDeletion(command.userId);
  }

  @GrpcMethod('ProfileService', 'CancelAccountDeletion')
  cancelAccountDeletion(command: { userId: string }) {
    return this.profile.cancelAccountDeletion(command.userId);
  }

  @GrpcMethod('ProfileService', 'GetAccountDeletionStatus')
  getAccountDeletionStatus(command: { userId: string }) {
    return this.profile.getAccountDeletionStatus(command.userId);
  }

  @GrpcMethod('ProfileService', 'SetE2EPublicKey')
  setE2EPublicKey(command: { userId: string; publicKey: string }) {
    return this.profile.setE2EPublicKey(command.userId, command.publicKey);
  }

  @GrpcMethod('ProfileService', 'GetE2EPublicKey')
  getE2EPublicKey(command: { userId: string }) {
    return this.profile.getE2EPublicKey(command.userId);
  }

  @GrpcMethod('DocumentsService', 'CreateDocument')
  createDocument(command: { userId: string; type: string; number: string; issuedAt?: string; expiresAt?: string; metadataJson?: string }) {
    return this.documents.create(command);
  }

  @GrpcMethod('DocumentsService', 'ListDocuments')
  listDocuments(command: { userId: string }) {
    return this.documents.list(command.userId);
  }

  @GrpcMethod('DocumentsService', 'GetDocument')
  getDocument(command: { documentId: string; userId: string }) {
    return this.documents.get(command.documentId, command.userId);
  }

  @GrpcMethod('DocumentsService', 'UpdateDocument')
  updateDocument(command: { documentId: string; userId: string; number?: string; issuedAt?: string; expiresAt?: string; metadataJson?: string }) {
    return this.documents.update(command);
  }

  @GrpcMethod('DocumentsService', 'DeleteDocument')
  deleteDocument(command: { documentId: string; userId: string }) {
    return this.documents.delete(command.documentId, command.userId);
  }

  @GrpcMethod('AddressesService', 'ListAddresses')
  listAddresses(command: { userId: string }) {
    return this.addresses.list(command.userId);
  }

  @GrpcMethod('AddressesService', 'UpsertAddress')
  upsertAddress(command: {
    userId: string;
    addressId?: string;
    label: string;
    city: string;
    street: string;
    house: string;
    apartment?: string;
    comment?: string;
    latitude?: number;
    longitude?: number;
    fullAddress?: string;
  }) {
    return this.addresses.upsert(command);
  }

  @GrpcMethod('AddressesService', 'DeleteAddress')
  deleteAddress(command: { addressId: string; userId: string }) {
    return this.addresses.delete(command.addressId, command.userId);
  }

  @GrpcMethod('MediaService', 'CreateAvatarUploadUrl')
  createAvatarUploadUrl(command: { userId: string; contentType: string }) {
    return this.media.createAvatarUploadUrl(command.userId, command.contentType);
  }

  @GrpcMethod('MediaService', 'ConfirmAvatar')
  confirmAvatar(command: { userId: string; storageKey: string }) {
    return this.media.confirmAvatar(command.userId, command.storageKey);
  }

  @GrpcMethod('MediaService', 'GetAvatarAccessUrl')
  getAvatarAccessUrl(command: { requesterId: string; targetUserId: string }) {
    return this.media.getAvatarAccessUrl(command.requesterId, command.targetUserId);
  }

  @GrpcMethod('MediaService', 'CreateDocumentPhotoUploadUrl')
  createDocumentPhotoUploadUrl(command: { userId: string; documentId: string; contentType: string }) {
    return this.media.createDocumentPhotoUploadUrl(command.userId, command.documentId, command.contentType);
  }

  @GrpcMethod('MediaService', 'GetDocumentPhotoAccessUrl')
  getDocumentPhotoAccessUrl(command: { requesterId: string; targetUserId: string; storageKey: string }) {
    return this.media.getDocumentPhotoAccessUrl(command.requesterId, command.targetUserId, command.storageKey);
  }

  @GrpcMethod('MediaService', 'ResolveStreamToken')
  resolveStreamToken(command: { token: string; requesterId?: string }) {
    return this.media.resolveStreamToken(command.token, command.requesterId);
  }

  @GrpcMethod('MediaService', 'CreateFamilyAvatarUploadUrl')
  createFamilyAvatarUploadUrl(command: { requesterId: string; groupId: string; contentType: string }) {
    return this.media.createFamilyAvatarUploadUrl(command.requesterId, command.groupId, command.contentType);
  }

  @GrpcMethod('MediaService', 'ConfirmFamilyAvatar')
  confirmFamilyAvatar(command: { requesterId: string; groupId: string; storageKey: string }) {
    return this.media.confirmFamilyAvatar(command.requesterId, command.groupId, command.storageKey);
  }

  @GrpcMethod('MediaService', 'GetFamilyAvatarAccessUrl')
  getFamilyAvatarAccessUrl(command: { requesterId: string; groupId: string }) {
    return this.media.getFamilyAvatarAccessUrl(command.requesterId, command.groupId);
  }

  @GrpcMethod('MediaService', 'CreateChatMediaUploadUrl')
  createChatMediaUploadUrl(command: { requesterId: string; roomId: string; contentType: string; fileName?: string }) {
    return this.media.createChatMediaUploadUrl(command.requesterId, command.roomId, command.contentType, command.fileName);
  }

  @GrpcMethod('MediaService', 'GetChatMediaAccessUrl')
  getChatMediaAccessUrl(command: { requesterId: string; roomId: string; storageKey: string; fileName?: string }) {
    return this.media.getChatMediaAccessUrl(command.requesterId, command.roomId, command.storageKey, command.fileName);
  }

  @GrpcMethod('MediaService', 'CreateChatRoomAvatarUploadUrl')
  createChatRoomAvatarUploadUrl(command: { requesterId: string; roomId: string; contentType: string }) {
    return this.media.createChatRoomAvatarUploadUrl(command.requesterId, command.roomId, command.contentType);
  }

  @GrpcMethod('MediaService', 'ConfirmChatRoomAvatar')
  confirmChatRoomAvatar(command: { requesterId: string; roomId: string; storageKey: string }) {
    return this.media.confirmChatRoomAvatar(command.requesterId, command.roomId, command.storageKey);
  }

  @GrpcMethod('MediaService', 'GetChatRoomAvatarAccessUrl')
  getChatRoomAvatarAccessUrl(command: { requesterId: string; roomId: string }) {
    return this.media.getChatRoomAvatarAccessUrl(command.requesterId, command.roomId);
  }

  @GrpcMethod('OAuthCoreService', 'Authorize')
  authorize(command: {
    userId: string;
    clientId: string;
    redirectUri: string;
    scope: string;
    state?: string;
    nonce?: string;
    grantConsent?: boolean;
  }) {
    return this.oauthCore.authorize(command);
  }

  @GrpcMethod('OAuthCoreService', 'Token')
  token(command: { grantType: string; code?: string; refreshToken?: string; clientId: string; clientSecret?: string; redirectUri?: string }) {
    return this.oauthCore.token(command);
  }

  @GrpcMethod('OAuthCoreService', 'UserInfo')
  userInfo(command: { accessToken: string }) {
    return this.oauthCore.userInfo(command.accessToken);
  }

  @GrpcMethod('OAuthCoreService', 'CheckOAuthConsent')
  checkOAuthConsent(command: { userId: string; clientId: string; scope: string }) {
    return this.oauthCore.checkConsent(command.userId, command.clientId, command.scope);
  }

  @GrpcMethod('OAuthCoreService', 'GrantOAuthConsent')
  grantOAuthConsent(command: { userId: string; clientId: string; scope: string }) {
    return this.oauthCore.grantConsent(command.userId, command.clientId, command.scope);
  }

  @GrpcMethod('OAuthCoreService', 'ListUserOAuthConsents')
  listUserOAuthConsents(command: { userId: string }) {
    return this.oauthCore.listUserConsents(command.userId);
  }

  @GrpcMethod('OAuthCoreService', 'RevokeOAuthConsent')
  revokeOAuthConsent(command: { userId: string; consentId: string }) {
    return this.oauthCore.revokeConsent(command.userId, command.consentId);
  }

  @GrpcMethod('OAuthCoreService', 'GetOAuthClientPublicInfo')
  getOAuthClientPublicInfo(command: { clientId: string }) {
    return this.oauthCore.getClientPublicInfo(command.clientId);
  }

  @GrpcMethod('OtpService', 'SendOtp')
  sendOtp(command: { target: string; channel: string; purpose: string; userId?: string }) {
    return this.otp.send(command);
  }

  @GrpcMethod('OtpService', 'VerifyOtp')
  verifyOtp(command: { target: string; code: string; purpose: string }) {
    return this.otp.verify(command);
  }

  @GrpcMethod('AdvancedAuthService', 'CreateWebAuthnRegistrationChallenge')
  createWebAuthnRegistrationChallenge(command: { userId: string }) {
    return this.advancedAuth.createWebAuthnRegistrationChallenge(command.userId);
  }

  @GrpcMethod('AdvancedAuthService', 'CreateWebAuthnLoginChallenge')
  createWebAuthnLoginChallenge(command: { userId: string }) {
    return this.advancedAuth.createWebAuthnLoginChallenge(command.userId);
  }

  @GrpcMethod('AdvancedAuthService', 'CreateQrSession')
  createQrSession(command: { deviceName: string; fingerprint?: string; deviceType?: string; ipAddress?: string; userAgent?: string }) {
    return this.advancedAuth.createQrSession({
      deviceName: command.deviceName,
      fingerprint: command.fingerprint ?? command.deviceName,
      deviceType: command.deviceType,
      ipAddress: command.ipAddress,
      userAgent: command.userAgent
    });
  }

  @GrpcMethod('AdvancedAuthService', 'PollQrSession')
  pollQrSession(command: { sessionId: string }) {
    return this.advancedAuth.pollQrSession(command.sessionId);
  }

  @GrpcMethod('AdvancedAuthService', 'ApproveQrSession')
  approveQrSession(command: { sessionId: string; userId: string }) {
    return this.advancedAuth.approveQrSession(command.sessionId, command.userId);
  }

  @GrpcMethod('FamilyService', 'CreateFamilyGroup')
  createFamilyGroup(command: { ownerId: string; name: string }) {
    return this.family.create(command.ownerId, command.name);
  }

  @GrpcMethod('FamilyService', 'ListFamilyGroups')
  listFamilyGroups(command: { userId: string }) {
    return this.family.list(command.userId);
  }

  @GrpcMethod('FamilyService', 'GetFamilyGroup')
  getFamilyGroup(command: { requesterId: string; groupId: string }) {
    return this.family.get(command.requesterId, command.groupId);
  }

  @GrpcMethod('FamilyService', 'UpdateFamilyGroup')
  updateFamilyGroup(command: { requesterId: string; groupId: string; name: string }) {
    return this.family.update(command.requesterId, command.groupId, command.name);
  }

  @GrpcMethod('FamilyService', 'DeleteFamilyGroup')
  deleteFamilyGroup(command: { requesterId: string; groupId: string }) {
    return this.family.delete(command.requesterId, command.groupId);
  }

  @GrpcMethod('FamilyService', 'AddFamilyMember')
  addFamilyMember(command: { groupId: string; userId: string; role: string }) {
    return this.family.addMember(command.groupId, command.userId, command.role);
  }

  @GrpcMethod('FamilyService', 'RemoveFamilyMember')
  removeFamilyMember(command: { requesterId: string; memberId: string }) {
    return this.family.removeMember(command.requesterId, command.memberId);
  }

  @GrpcMethod('FamilyService', 'SendFamilyInvite')
  async sendFamilyInvite(command: { requesterId: string; groupId: string; target?: string; inviteeUserId?: string }) {
    const invite = await this.family.sendInvite(command.requesterId, command.groupId, {
      target: command.target,
      inviteeUserId: command.inviteeUserId
    });
    if (invite.status === 'ACCEPTED') {
      if (invite.inviteeUserId) {
        await this.chat.ensureBotRoomForMember(command.groupId, command.requesterId, invite.inviteeUserId);
      }
    }
    return invite;
  }

  @GrpcMethod('FamilyService', 'SearchFamilyInviteUsers')
  searchFamilyInviteUsers(command: { requesterId: string; groupId: string; query: string }) {
    return this.family.searchInviteUsers(command.requesterId, command.groupId, command.query);
  }

  @GrpcMethod('FamilyService', 'RespondFamilyInvite')
  respondFamilyInvite(command: { userId: string; inviteId: string; accept: boolean }) {
    return this.family.respondInvite(command.userId, command.inviteId, command.accept);
  }

  @GrpcMethod('FamilyService', 'ListFamilyInvites')
  listFamilyInvites(command: { userId: string; groupId?: string }) {
    return this.family.listInvites(command.userId, command.groupId);
  }

  @GrpcMethod('FamilyService', 'GetFamilyPresence')
  getFamilyPresence(command: { requesterId: string; groupId: string }) {
    return this.presence.getFamilyPresence(command.requesterId, command.groupId);
  }

  @GrpcMethod('NotificationsService', 'ListNotifications')
  listNotifications(command: { userId: string; limit?: number; unreadOnly?: boolean }) {
    return this.notifications.list(command.userId, command.limit, command.unreadOnly);
  }

  @GrpcMethod('NotificationsService', 'GetUnreadCount')
  getUnreadCount(command: { userId: string }) {
    return this.notifications.unreadCount(command.userId);
  }

  @GrpcMethod('NotificationsService', 'MarkNotificationRead')
  markNotificationRead(command: { userId: string; notificationId: string }) {
    return this.notifications.markRead(command.userId, command.notificationId);
  }

  @GrpcMethod('NotificationsService', 'MarkAllNotificationsRead')
  markAllNotificationsRead(command: { userId: string }) {
    return this.notifications.markAllRead(command.userId);
  }

  @GrpcMethod('NotificationsService', 'DeleteNotification')
  deleteNotification(command: { userId: string; notificationId: string }) {
    return this.notifications.delete(command.userId, command.notificationId);
  }

  @GrpcMethod('NotificationsService', 'DeleteAllNotifications')
  deleteAllNotifications(command: { userId: string }) {
    return this.notifications.deleteAll(command.userId);
  }

  @GrpcMethod('ChatService', 'ListRooms')
  listChatRooms(command: { userId: string; groupId: string }) {
    return this.chat.listRooms(command.userId, command.groupId);
  }

  @GrpcMethod('ChatService', 'CreateRoom')
  createChatRoom(command: { userId: string; groupId: string; name: string; memberUserIds?: string[] }) {
    return this.chat.createRoom(command.userId, command.groupId, command.name, command.memberUserIds ?? []);
  }

  @GrpcMethod('ChatService', 'CreateE2ERoom')
  createE2EChatRoom(command: { userId: string; groupId: string; peerUserId: string }) {
    return this.chat.createE2ERoom(command.userId, command.groupId, command.peerUserId);
  }

  @GrpcMethod('ChatService', 'UpdateRoomSettings')
  updateChatRoomSettings(command: { userId: string; roomId: string; name?: string; notificationsMuted?: boolean; pinned?: boolean }) {
    return this.chat.updateRoomSettings(command.userId, command.roomId, command.name, command.notificationsMuted, command.pinned);
  }

  @GrpcMethod('ChatService', 'AddRoomMember')
  addChatRoomMember(command: { userId: string; roomId: string; memberUserId: string }) {
    return this.chat.addRoomMember(command.userId, command.roomId, command.memberUserId);
  }

  @GrpcMethod('ChatService', 'RemoveRoomMember')
  removeChatRoomMember(command: { userId: string; roomId: string; memberUserId: string }) {
    return this.chat.removeRoomMember(command.userId, command.roomId, command.memberUserId);
  }

  @GrpcMethod('ChatService', 'ListMessages')
  listChatMessages(command: { userId: string; roomId: string; beforeMessageId?: string; limit?: number }) {
    return this.chat.listMessages(command.userId, command.roomId, command.beforeMessageId, command.limit);
  }

  @GrpcMethod('ChatService', 'SendMessage')
  sendChatMessage(command: {
    userId: string;
    roomId: string;
    type: string;
    content?: string;
    replyToId?: string;
    storageKey?: string;
    mimeType?: string;
    metadataJson?: string;
    poll?: { question: string; options: string[]; allowsMultiple?: boolean; isAnonymous?: boolean };
    isEncrypted?: boolean;
  }) {
    return this.chat.sendMessage(
      command.userId,
      command.roomId,
      command.type,
      command.content,
      command.replyToId,
      command.storageKey,
      command.mimeType,
      command.metadataJson,
      command.poll,
      command.isEncrypted
    );
  }

  @GrpcMethod('ChatService', 'VotePoll')
  voteChatPoll(command: { userId: string; messageId: string; optionIds?: string[] }) {
    return this.chat.votePoll(command.userId, command.messageId, command.optionIds ?? []);
  }

  @GrpcMethod('ChatService', 'SetRoomNotificationsMuted')
  setRoomNotificationsMuted(command: { userId: string; roomId: string; muted: boolean }) {
    return this.chat.setRoomNotificationsMuted(command.userId, command.roomId, command.muted);
  }

  @GrpcMethod('ChatService', 'DeleteRoom')
  deleteChatRoom(command: { userId: string; roomId: string }) {
    return this.chat.deleteRoom(command.userId, command.roomId);
  }

  @GrpcMethod('ChatService', 'EditMessage')
  editChatMessage(command: { userId: string; messageId: string; content: string }) {
    return this.chat.editMessage(command.userId, command.messageId, command.content);
  }

  @GrpcMethod('ChatService', 'DeleteMessage')
  deleteChatMessage(command: { userId: string; messageId: string }) {
    return this.chat.deleteMessage(command.userId, command.messageId);
  }

  @GrpcMethod('ChatService', 'SetMessagePinned')
  setChatMessagePinned(command: { userId: string; messageId: string; pinned: boolean }) {
    return this.chat.setMessagePinned(command.userId, command.messageId, Boolean(command.pinned));
  }

  @GrpcMethod('ChatService', 'ToggleMessageReaction')
  toggleMessageReaction(command: { userId: string; messageId: string; emoji: string }) {
    return this.chat.toggleMessageReaction(command.userId, command.messageId, command.emoji);
  }

  @GrpcMethod('ChatService', 'ForwardMessages')
  forwardMessages(command: { userId: string; targetRoomId: string; messageIds: string[] }) {
    return this.chat.forwardMessages(command.userId, command.targetRoomId, command.messageIds ?? []);
  }

  @GrpcMethod('ChatService', 'MarkRoomRead')
  markChatRoomRead(command: { userId: string; roomId: string; lastMessageId?: string }) {
    return this.chat.markRoomRead(command.userId, command.roomId, command.lastMessageId);
  }

  @GrpcMethod('ChatService', 'ReportTyping')
  reportTyping(command: { userId: string; roomId: string }) {
    return this.chat.reportTyping(command.userId, command.roomId);
  }

  @GrpcMethod('BotService', 'CreateBot')
  createBot(command: { ownerId: string; name: string; username: string }) {
    return this.botFather.createBot(command.ownerId, command.name, command.username);
  }

  @GrpcMethod('BotService', 'RevokeBotToken')
  revokeBotToken(command: { requesterId: string; botId: string; isSuperAdmin?: boolean }) {
    return this.botFather.revokeToken(command.requesterId, command.botId, Boolean(command.isSuperAdmin));
  }

  @GrpcMethod('BotService', 'SetBotWebApp')
  setBotWebApp(command: { requesterId: string; botId: string; webAppUrl?: string; isSuperAdmin?: boolean }) {
    return this.botFather.setWebApp(command.requesterId, command.botId, command.webAppUrl, Boolean(command.isSuperAdmin));
  }

  @GrpcMethod('BotService', 'ListMyBots')
  listMyBots(command: { ownerId: string }) {
    return this.botFather.listMyBots(command.ownerId);
  }

  @GrpcMethod('BotService', 'GetBot')
  getBot(command: { requesterId: string; botId: string; isSuperAdmin?: boolean }) {
    return this.botFather.getBot(command.requesterId, command.botId, Boolean(command.isSuperAdmin));
  }

  @GrpcMethod('BotService', 'UpdateBot')
  updateBot(command: { requesterId: string; botId: string; name?: string; username?: string; isSuperAdmin?: boolean }) {
    return this.botFather.updateBot(command.requesterId, command.botId, command, Boolean(command.isSuperAdmin));
  }

  @GrpcMethod('BotService', 'UpdateBotProfile')
  updateBotProfile(command: {
    requesterId: string;
    botId: string;
    description?: string;
    aboutText?: string;
    botPicUrl?: string;
    menuButtonJson?: string;
    menuButtonUrl?: string;
    menuButtonText?: string;
    isSuperAdmin?: boolean;
  }) {
    const patch: {
      description?: string | null;
      aboutText?: string | null;
      botPicUrl?: string | null;
      menuButton?: unknown;
    } = {
      description: command.description,
      aboutText: command.aboutText,
      botPicUrl: command.botPicUrl
    };

    if (command.menuButtonJson !== undefined) {
      patch.menuButton = command.menuButtonJson.trim() ? command.menuButtonJson : { type: 'default' };
    } else if (command.menuButtonUrl !== undefined) {
      const url = command.menuButtonUrl.trim();
      patch.menuButton = url
        ? command.menuButtonText?.trim()
          ? `${url}|${command.menuButtonText.trim()}`
          : url
        : { type: 'default' };
    }

    return this.botFather.updateBotProfile(command.requesterId, command.botId, patch, Boolean(command.isSuperAdmin));
  }

  @GrpcMethod('BotService', 'DeleteBot')
  deleteBot(command: { requesterId: string; botId: string; isSuperAdmin?: boolean }) {
    return this.botFather.deleteBot(command.requesterId, command.botId, Boolean(command.isSuperAdmin));
  }

  @GrpcMethod('BotService', 'ListAllBots')
  listAllBots(command: { requesterId: string; isSuperAdmin?: boolean; search?: string; page?: number; limit?: number }) {
    return this.botFather.listAllBots(
      command.requesterId,
      Boolean(command.isSuperAdmin),
      command.search,
      Number(command.page) || 1,
      Number(command.limit) || 20
    );
  }

  @GrpcMethod('BotService', 'SetBotActive')
  setBotActive(command: { requesterId: string; botId: string; isActive: boolean; isSuperAdmin?: boolean }) {
    return this.botFather.setBotActive(command.requesterId, command.botId, command.isActive, Boolean(command.isSuperAdmin));
  }

  @GrpcMethod('BotService', 'GetBotMetrics')
  getBotMetrics(command: { requesterId: string; isSuperAdmin?: boolean }) {
    return this.botFather.getBotMetrics(command.requesterId, Boolean(command.isSuperAdmin));
  }

  @GrpcMethod('BotService', 'ValidateBotToken')
  async validateBotToken(command: { token: string }) {
    const bot = await this.botFather.validateBotToken(command.token);
    return bot ?? {};
  }

  @GrpcMethod('BotService', 'ExecuteBotMethod')
  executeBotMethod(command: { token: string; method: string; payloadJson: string }) {
    const payload = command.payloadJson ? (JSON.parse(command.payloadJson) as Record<string, unknown>) : {};
    return this.botApi.executeMethod(command.token, command.method, payload);
  }

  @GrpcMethod('BotService', 'ValidateWebAppInitData')
  validateWebAppInitData(command: { initData: string; botToken: string }) {
    return this.botApi.validateWebAppInitData(command.initData, command.botToken);
  }

  @GrpcMethod('BotService', 'SubmitBotInboundMessage')
  submitBotInboundMessage(command: { senderUserId: string; botRef: string; text: string; roomId?: string }) {
    return this.botInbound.submitUserMessage(command.senderUserId, command.botRef, command.text, command.roomId);
  }

  @GrpcMethod('BotService', 'SubmitBotCallbackQuery')
  submitBotCallbackQuery(command: { senderUserId: string; botRef: string; messageId: number; callbackData: string }) {
    return this.botInbound.submitCallbackQuery(command.senderUserId, command.botRef, command.messageId, command.callbackData);
  }

  @GrpcMethod('BotService', 'ListBotChatMessages')
  listBotChatMessages(command: { userId: string; botRef: string; roomId?: string }) {
    return this.botApi.listBotChatMessages(command.userId, command.botRef, command.roomId);
  }

  private toSessionResponse(session: { id: string; userId: string; deviceId: string | null; pinVerified: boolean; status: string; ipAddress: string | null; userAgent: string | null; expiresAt: Date; createdAt: Date; updatedAt: Date }) {
    return {
      id: session.id,
      userId: session.userId,
      deviceId: session.deviceId,
      pinVerified: session.pinVerified,
      status: session.status,
      ipAddress: session.ipAddress,
      userAgent: session.userAgent,
      expiresAt: session.expiresAt.toISOString(),
      createdAt: session.createdAt.toISOString(),
      updatedAt: session.updatedAt.toISOString()
    };
  }

  private toLinkedAccountResponse(account: { id: string; userId: string; providerName: string; providerId: string; email: string | null; displayName: string | null; avatarUrl: string | null; createdAt: Date; updatedAt: Date }) {
    return {
      ...account,
      createdAt: account.createdAt.toISOString(),
      updatedAt: account.updatedAt.toISOString()
    };
  }

  private toSocialProviderResponse(provider: { id: string; providerName: string; clientId: string; clientSecret: string | null; isEnabled: boolean; createdAt: Date; updatedAt: Date }) {
    return {
      ...provider,
      createdAt: provider.createdAt.toISOString(),
      updatedAt: provider.updatedAt.toISOString()
    };
  }
}
