import { Body, Controller, Delete, Get, HttpCode, Param, Post, UseGuards } from "@nestjs/common";
import { ApiBearerAuth, ApiOperation, ApiResponse, ApiTags } from "@nestjs/swagger";
import { ApiKeyEnvironment, ApiKeyScope, ApiKeyType } from "@prisma/client";
import { IsEnum, IsOptional, IsString, Length } from "class-validator";
import { ApiProperty } from "@nestjs/swagger";
import { AdminUserGuard } from "../admin-auth/admin-user.guard";
import { ApiErrorResponse } from "../common/swagger/api-error.schema";
import { ApiKeyService } from "./api-key.service";
import { ApiKeyDto, CreatedApiKeyDto } from "./dto/api-key.dto";
import { CreateApiKeyDto } from "./dto/create-api-key.dto";

class CreateApiKeyPairDto {
  @ApiProperty({ enum: ApiKeyEnvironment, example: ApiKeyEnvironment.test })
  @IsEnum(ApiKeyEnvironment)
  environment!: ApiKeyEnvironment;

  @ApiProperty({ required: false, example: "Conexión del ERP" })
  @IsOptional()
  @IsString()
  @Length(1, 80)
  label?: string;
}

class CreatedApiKeyPairDto {
  @ApiProperty({ type: CreatedApiKeyDto })
  secret!: CreatedApiKeyDto;

  @ApiProperty({ type: CreatedApiKeyDto })
  publishable!: CreatedApiKeyDto;
}

@ApiTags("Admin · API Keys")
@ApiBearerAuth("adminUserAuth")
@ApiResponse({
  status: 401,
  description: "Token admin faltante o inválido.",
  type: ApiErrorResponse,
})
@ApiResponse({ status: 429, description: "Rate limit excedido.", type: ApiErrorResponse })
@Controller("admin/merchants/:merchantId/api-keys")
@UseGuards(AdminUserGuard)
export class ApiKeyAdminController {
  constructor(private readonly service: ApiKeyService) {}

  @Get()
  @ApiOperation({
    operationId: "adminApiKeysList",
    summary: "Listar API keys de un merchant",
    description:
      "Devuelve metadata de las API keys (prefix, tipo, ambiente, etiqueta, fechas). " +
      "El valor completo (`secret`) **no** se devuelve nunca — solo en la creación.",
  })
  @ApiResponse({ status: 200, type: [ApiKeyDto] })
  list(@Param("merchantId") merchantId: string) {
    return this.service.list(merchantId);
  }

  @Post()
  @ApiOperation({
    operationId: "adminApiKeysCreate",
    summary: "Emitir una API key suelta de un merchant",
    description:
      "Emite una API key individual (privada o pública). " +
      "Para alta de un comercio nuevo conviene usar `POST .../pair` que genera el par de una vez. " +
      "El valor completo (`secret`) se devuelve **una sola vez** en esta respuesta.",
  })
  @ApiResponse({ status: 201, type: CreatedApiKeyDto })
  @ApiResponse({ status: 400, description: "Validación de body falló.", type: ApiErrorResponse })
  @ApiResponse({ status: 404, description: "Merchant no encontrado.", type: ApiErrorResponse })
  create(@Param("merchantId") merchantId: string, @Body() dto: CreateApiKeyDto) {
    return this.service.create(merchantId, dto);
  }

  @Post("pair")
  @ApiOperation({
    operationId: "adminApiKeysCreatePair",
    summary: "Emitir par de claves (privada + pública) de un ambiente",
    description:
      "Genera de una sola vez la **clave privada** y la **clave pública** del mismo ambiente. " +
      "Es el flujo recomendado para conectar un ERP, que necesita las dos. " +
      "Los valores completos (`secret`) se devuelven **una sola vez** en esta respuesta.",
  })
  @ApiResponse({ status: 201, type: CreatedApiKeyPairDto })
  @ApiResponse({ status: 400, description: "Validación de body falló.", type: ApiErrorResponse })
  @ApiResponse({ status: 404, description: "Merchant no encontrado.", type: ApiErrorResponse })
  async createPair(
    @Param("merchantId") merchantId: string,
    @Body() dto: CreateApiKeyPairDto,
  ): Promise<CreatedApiKeyPairDto> {
    const baseLabel = dto.label?.trim();
    const secret = await this.service.create(merchantId, {
      type: ApiKeyType.secret,
      environment: dto.environment,
      scope: ApiKeyScope.full,
      label: baseLabel ? `${baseLabel} · privada` : undefined,
    });
    const publishable = await this.service.create(merchantId, {
      type: ApiKeyType.publishable,
      environment: dto.environment,
      scope: ApiKeyScope.full,
      label: baseLabel ? `${baseLabel} · pública` : undefined,
    });
    return { secret, publishable };
  }

  @Delete(":id")
  @HttpCode(204)
  @ApiOperation({
    operationId: "adminApiKeysRevoke",
    summary: "Revocar una API key",
    description: "Marca la key como revocada. Las nuevas requests con esa key serán rechazadas.",
  })
  @ApiResponse({ status: 204, description: "Revocada (o ya estaba revocada)." })
  @ApiResponse({ status: 404, description: "Key no encontrada.", type: ApiErrorResponse })
  async revoke(@Param("merchantId") merchantId: string, @Param("id") id: string) {
    await this.service.revoke(merchantId, id);
  }
}
