import { Body, Controller, Delete, Get, Param, Post, Put, UseGuards, UsePipes, ValidationPipe } from '@nestjs/common';
import { ApiBearerAuth, ApiBody, ApiOperation, ApiParam, ApiResponse, ApiTags } from '@nestjs/swagger';
import { map } from 'rxjs';
import { CoreGrpcService } from '../core-grpc.service';
import { CurrentAdmin } from '../decorators/current-admin.decorator';
import { ConnectLinkedAccountDto, TestMessagingDeliveryDto, UpsertSettingDto, UpsertSocialProviderDto } from '../dto/settings.dto';
import { AdminGuard, AdminRequestUser, assertAdminPermission } from '../guards/admin.guard';

const settingsWritePipe = new ValidationPipe({
  whitelist: true,
  transform: true,
  forbidNonWhitelisted: false
});

@ApiTags('Глобальные настройки')
@ApiBearerAuth()
@UseGuards(AdminGuard)
@Controller('admin/settings')
export class SettingsController {
  constructor(private readonly core: CoreGrpcService) {}

  @Get()
  @ApiOperation({ summary: 'Список системных настроек', description: 'Возвращает динамические настройки, включая PIN timeout и OAuth providers.' })
  @ApiResponse({ status: 200, description: 'Список настроек получен' })
  list(@CurrentAdmin() admin: AdminRequestUser) {
    assertAdminPermission(admin, 'canManageSettings');
    return this.core.settings.ListSettings({}).pipe(
      map((response) => {
        const payload = response as { settings?: unknown[] };
        return { settings: payload.settings ?? [] };
      })
    );
  }

  @Get('key/:key')
  @ApiOperation({ summary: 'Получить настройку', description: 'Возвращает SystemSetting по ключу.' })
  @ApiParam({ name: 'key', description: 'Ключ настройки', example: 'PIN_LOCK_TIMEOUT_MINUTES' })
  @ApiResponse({ status: 200, description: 'Настройка получена' })
  get(@Param('key') key: string, @CurrentAdmin() admin: AdminRequestUser) {
    assertAdminPermission(admin, 'canManageSettings');
    return this.core.settings.GetSetting({ key });
  }

  @Put()
  @UsePipes(settingsWritePipe)
  @ApiOperation({ summary: 'Создать или обновить настройку', description: 'Меняет значение SystemSetting без изменения кода frontend.' })
  @ApiBody({ type: UpsertSettingDto })
  @ApiResponse({ status: 200, description: 'Настройка создана или обновлена' })
  upsert(@Body() dto: UpsertSettingDto, @CurrentAdmin() admin: AdminRequestUser) {
    assertAdminPermission(admin, 'canManageSettings');
    return this.core.settings.UpsertSetting({
      key: dto.key,
      value: dto.value,
      description: dto.description,
      isSecret: dto.isSecret
    });
  }

  @Delete('key/:key')
  @ApiOperation({ summary: 'Удалить настройку', description: 'Удаляет SystemSetting по ключу.' })
  @ApiParam({ name: 'key', description: 'Ключ настройки' })
  @ApiResponse({ status: 200, description: 'Настройка удалена' })
  delete(@Param('key') key: string, @CurrentAdmin() admin: AdminRequestUser) {
    assertAdminPermission(admin, 'canManageSettings');
    return this.core.settings.DeleteSetting({ key });
  }

  @Get('oauth/providers')
  @ApiOperation({ summary: 'Список OAuth провайдеров', description: 'Возвращает глобальные настройки Google/Yandex и других social login providers.' })
  @ApiResponse({ status: 200, description: 'Список провайдеров получен' })
  listProviders(@CurrentAdmin() admin: AdminRequestUser) {
    assertAdminPermission(admin, 'canManageSettings');
    return this.core.settings.ListSocialProviders({});
  }

  @Put('oauth/providers')
  @UsePipes(settingsWritePipe)
  @ApiOperation({ summary: 'Создать или обновить OAuth провайдера', description: 'Управляет clientId/clientSecret и глобальным включением Google/Yandex входа.' })
  @ApiBody({ type: UpsertSocialProviderDto })
  @ApiResponse({ status: 200, description: 'Провайдер создан или обновлен' })
  upsertProvider(@Body() dto: UpsertSocialProviderDto, @CurrentAdmin() admin: AdminRequestUser) {
    assertAdminPermission(admin, 'canManageSettings');
    return this.core.settings.UpsertSocialProvider({
      providerName: dto.providerName,
      clientId: dto.clientId,
      clientSecret: dto.clientSecret,
      isEnabled: dto.isEnabled
    });
  }

  @Delete('oauth/providers/:providerName')
  @ApiOperation({ summary: 'Удалить OAuth провайдера', description: 'Удаляет глобальную настройку social login provider.' })
  @ApiParam({ name: 'providerName', description: 'Название провайдера', example: 'google' })
  @ApiResponse({ status: 200, description: 'Провайдер удален' })
  deleteProvider(@Param('providerName') providerName: string, @CurrentAdmin() admin: AdminRequestUser) {
    assertAdminPermission(admin, 'canManageSettings');
    return this.core.settings.DeleteSocialProvider({ providerName });
  }

  @Post('messaging/test')
  @UsePipes(settingsWritePipe)
  @ApiOperation({ summary: 'Тест email/SMS', description: 'Отправляет тестовый OTP-код через настроенного провайдера.' })
  @ApiBody({ type: TestMessagingDeliveryDto })
  @ApiResponse({ status: 200, description: 'Тестовое сообщение отправлено' })
  testMessaging(@Body() dto: TestMessagingDeliveryDto, @CurrentAdmin() admin: AdminRequestUser) {
    assertAdminPermission(admin, 'canManageSettings');
    return this.core.settings.TestMessagingDelivery({ channel: dto.channel, target: dto.target });
  }

  @Get('linked-accounts/users/:userId')
  @ApiOperation({ summary: 'Связанные внешние аккаунты', description: 'Возвращает LinkedAccount записи пользователя для Google/Yandex и других провайдеров.' })
  @ApiParam({ name: 'userId', description: 'ID пользователя' })
  @ApiResponse({ status: 200, description: 'Список внешних аккаунтов получен' })
  listLinkedAccounts(@Param('userId') userId: string) {
    return this.core.security.ListLinkedAccounts({ userId });
  }

  @Put('linked-accounts/users/:userId')
  @ApiOperation({ summary: 'Подключить внешний аккаунт', description: 'Создает или обновляет LinkedAccount для social OAuth логина пользователя.' })
  @ApiParam({ name: 'userId', description: 'ID пользователя' })
  @ApiBody({ type: ConnectLinkedAccountDto })
  @ApiResponse({ status: 200, description: 'Внешний аккаунт подключен' })
  connectLinkedAccount(@Param('userId') userId: string, @Body() dto: ConnectLinkedAccountDto) {
    return this.core.security.ConnectLinkedAccount({ userId, ...dto });
  }

  @Delete('linked-accounts/users/:userId/:providerName')
  @ApiOperation({ summary: 'Отключить внешний аккаунт', description: 'Удаляет связь пользователя с Google/Yandex или другим social provider.' })
  @ApiParam({ name: 'userId', description: 'ID пользователя' })
  @ApiParam({ name: 'providerName', description: 'Название провайдера', example: 'google' })
  @ApiResponse({ status: 200, description: 'Внешний аккаунт отключен' })
  disconnectLinkedAccount(@Param('userId') userId: string, @Param('providerName') providerName: string) {
    return this.core.security.DisconnectLinkedAccount({ userId, providerName });
  }
}
