import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
import {
  IsString,
  IsOptional,
  IsUUID,
  IsDateString,
  IsNumber,
  Min,
  Max,
  IsEnum,
  ValidateNested,
  Type,
} from 'class-validator';
import { Transform } from 'class-transformer';

// Enum para ordenamiento
export enum SortOrder {
  ASC = 'asc',
  DESC = 'desc',
}

// DTO base para paginación
export class PaginationDto {
  @ApiProperty({
    description: 'Número de página',
    minimum: 1,
    default: 1,
    example: 1,
  })
  @IsNumber({}, { message: 'La página debe ser un número' })
  @Min(1, { message: 'La página debe ser mayor a 0' })
  @Transform(({ value }) => parseInt(value, 10))
  page: number = 1;

  @ApiProperty({
    description: 'Número de elementos por página',
    minimum: 1,
    maximum: 100,
    default: 10,
    example: 10,
  })
  @IsNumber({}, { message: 'El límite debe ser un número' })
  @Min(1, { message: 'El límite debe ser mayor a 0' })
  @Max(100, { message: 'El límite no puede ser mayor a 100' })
  @Transform(({ value }) => parseInt(value, 10))
  limit: number = 10;

  @ApiPropertyOptional({
    description: 'Campo por el cual ordenar',
    example: 'createdAt',
  })
  @IsOptional()
  @IsString({ message: 'El campo de ordenamiento debe ser una cadena' })
  sortBy?: string;

  @ApiProperty({
    description: 'Orden de clasificación',
    enum: SortOrder,
    default: SortOrder.ASC,
    example: SortOrder.ASC,
  })
  @IsEnum(SortOrder, { message: 'El orden debe ser asc o desc' })
  sortOrder: SortOrder = SortOrder.ASC;

  // Método para calcular el offset
  get offset(): number {
    return (this.page - 1) * this.limit;
  }
}

// DTO base para búsqueda
export class SearchDto {
  @ApiPropertyOptional({
    description: 'Término de búsqueda',
    example: 'usuario',
  })
  @IsOptional()
  @IsString({ message: 'La consulta debe ser una cadena' })
  query?: string;

  @ApiPropertyOptional({
    description: 'Filtros adicionales como objeto JSON',
    example: { status: 'active', role: 'user' },
  })
  @IsOptional()
  filters?: Record<string, any>;
}

// DTO combinado para paginación y búsqueda
export class PaginatedSearchDto extends PaginationDto {
  @ApiPropertyOptional({
    description: 'Término de búsqueda',
    example: 'usuario',
  })
  @IsOptional()
  @IsString({ message: 'La consulta debe ser una cadena' })
  query?: string;

  @ApiPropertyOptional({
    description: 'Filtros adicionales como objeto JSON',
    example: { status: 'active', role: 'user' },
  })
  @IsOptional()
  filters?: Record<string, any>;
}

// DTO base para rangos de fechas
export class DateRangeDto {
  @ApiProperty({
    description: 'Fecha de inicio',
    type: 'string',
    format: 'date-time',
    example: '2023-01-01T00:00:00.000Z',
  })
  @IsDateString({}, { message: 'La fecha de inicio debe ser una fecha válida' })
  startDate: string;

  @ApiProperty({
    description: 'Fecha de fin',
    type: 'string',
    format: 'date-time',
    example: '2023-12-31T23:59:59.999Z',
  })
  @IsDateString({}, { message: 'La fecha de fin debe ser una fecha válida' })
  endDate: string;
}

// DTO base para IDs
export class IdDto {
  @ApiProperty({
    description: 'Identificador único',
    format: 'uuid',
    example: '123e4567-e89b-12d3-a456-426614174000',
  })
  @IsUUID(4, { message: 'El ID debe ser un UUID válido' })
  id: string;
}

// DTO base para respuestas
export class BaseResponseDto {
  @ApiProperty({
    description: 'Identificador único',
    format: 'uuid',
    example: '123e4567-e89b-12d3-a456-426614174000',
  })
  id: string;

  @ApiProperty({
    description: 'Fecha de creación',
    type: 'string',
    format: 'date-time',
    example: '2023-01-01T00:00:00.000Z',
  })
  createdAt: Date;

  @ApiProperty({
    description: 'Fecha de última actualización',
    type: 'string',
    format: 'date-time',
    example: '2023-01-01T00:00:00.000Z',
  })
  updatedAt: Date;
}

// DTO para respuestas paginadas
export class PaginatedResponseDto<T> {
  @ApiProperty({ description: 'Datos de la página actual' })
  data: T[];

  @ApiProperty({ description: 'Información de paginación' })
  pagination: {
    page: number;
    limit: number;
    total: number;
    totalPages: number;
    hasNext: boolean;
    hasPrev: boolean;
  };

  constructor(data: T[], pagination: PaginationDto, total: number) {
    this.data = data;
    const totalPages = Math.ceil(total / pagination.limit);
    
    this.pagination = {
      page: pagination.page,
      limit: pagination.limit,
      total,
      totalPages,
      hasNext: pagination.page < totalPages,
      hasPrev: pagination.page > 1,
    };
  }
}

// DTO para respuestas de error
export class ErrorResponseDto {
  @ApiProperty({ description: 'Código de estado HTTP' })
  statusCode: number;

  @ApiProperty({ description: 'Mensaje de error' })
  message: string | string[];

  @ApiProperty({ description: 'Timestamp del error' })
  timestamp: string;

  @ApiProperty({ description: 'Ruta donde ocurrió el error' })
  path: string;

  @ApiPropertyOptional({ description: 'Detalles adicionales del error' })
  details?: any;
}

// DTO para respuestas de éxito
export class SuccessResponseDto<T = any> {
  @ApiProperty({ description: 'Indica si la operación fue exitosa' })
  success: boolean = true;

  @ApiProperty({ description: 'Mensaje de éxito' })
  message: string;

  @ApiPropertyOptional({ description: 'Datos de respuesta' })
  data?: T;

  @ApiProperty({ description: 'Timestamp de la respuesta' })
  timestamp: string = new Date().toISOString();
}

// DTO base para operaciones de creación
export abstract class CreateBaseDto {
  // Campos comunes que pueden ser heredados
}

// DTO base para operaciones de actualización
export abstract class UpdateBaseDto {
  @ApiProperty({
    description: 'Identificador único del recurso a actualizar',
    format: 'uuid',
    example: '123e4567-e89b-12d3-a456-426614174000',
  })
  @IsUUID(4, { message: 'El ID debe ser un UUID válido' })
  id: string;
}

// Utilidades para DTOs
export class DtoUtils {
  static createPaginatedResponse<T>(
    data: T[],
    pagination: PaginationDto,
    total: number
  ): PaginatedResponseDto<T> {
    return new PaginatedResponseDto(data, pagination, total);
  }

  static createSuccessResponse<T>(
    message: string,
    data?: T
  ): SuccessResponseDto<T> {
    const response = new SuccessResponseDto<T>();
    response.message = message;
    if (data !== undefined) {
      response.data = data;
    }
    return response;
  }

  static createErrorResponse(
    statusCode: number,
    message: string | string[],
    path: string,
    details?: any
  ): ErrorResponseDto {
    return {
      statusCode,
      message,
      timestamp: new Date().toISOString(),
      path,
      details,
    };
  }
}