import { ApiPropertyOptional } from '@nestjs/swagger';
import { Transform, Type } from 'class-transformer';
import {
  IsBoolean,
  IsDateString,
  IsIn,
  IsInt,
  IsOptional,
  IsString,
  Max,
  MaxLength,
  Min,
} from 'class-validator';

// Whitelist of columns the client is allowed to sort by. We never feed an
// arbitrary string into Prisma's orderBy, so this list also doubles as the
// runtime guard for that mapping.
export const ALLOWED_SORT_FIELDS = [
  'id',
  'title',
  'createdAt',
  'updatedAt',
] as const;
export type IssueSortField = (typeof ALLOWED_SORT_FIELDS)[number];

export const ALLOWED_SORT_ORDERS = ['asc', 'desc'] as const;
export type IssueSortOrder = (typeof ALLOWED_SORT_ORDERS)[number];

// Coerce common "truthy"/"falsy" query-string values into a real boolean.
// Express delivers query params as strings, so without this `IsBoolean` would
// always fail.
const toBoolean = ({ value }: { value: unknown }): unknown => {
  if (value === 'true' || value === true || value === 1 || value === '1') {
    return true;
  }
  if (value === 'false' || value === false || value === 0 || value === '0') {
    return false;
  }
  return value;
};

// Query parameters for the paginated/list endpoint. Every field is optional;
// the service applies sensible defaults when fields are omitted.
export class QueryWorkingGroupIssuesDto {
  // --- Pagination -------------------------------------------------------

  @ApiPropertyOptional({ example: 1, description: '1-based page number.' })
  @IsOptional()
  @Type(() => Number)
  @IsInt()
  @Min(1)
  page?: number;

  @ApiPropertyOptional({
    example: 10,
    description: 'Items per page. Defaults to 10, capped at 50.',
  })
  @IsOptional()
  @Type(() => Number)
  @IsInt()
  @Min(1)
  @Max(50)
  limit?: number;

  // --- Search -----------------------------------------------------------

  @ApiPropertyOptional({
    description:
      'Case-insensitive text search across title, description, and recommendation.',
  })
  @IsOptional()
  @IsString()
  @MaxLength(200)
  @Transform(({ value }: { value: unknown }) =>
    typeof value === 'string' ? value.trim() : value,
  )
  search?: string;

  // --- Sorting ----------------------------------------------------------

  @ApiPropertyOptional({
    enum: ALLOWED_SORT_FIELDS,
    example: 'createdAt',
    description: 'Column to sort by. Defaults to createdAt.',
  })
  @IsOptional()
  @IsIn(ALLOWED_SORT_FIELDS)
  sortBy?: IssueSortField;

  @ApiPropertyOptional({
    enum: ALLOWED_SORT_ORDERS,
    example: 'desc',
    description: 'Sort direction. Defaults to desc.',
  })
  @IsOptional()
  @IsIn(ALLOWED_SORT_ORDERS)
  sortOrder?: IssueSortOrder;

  // --- Filters: direct foreign keys ------------------------------------

  @ApiPropertyOptional({ example: 1 })
  @IsOptional()
  @Type(() => Number)
  @IsInt()
  @Min(1)
  issueStatusId?: number;

  @ApiPropertyOptional({ example: 1 })
  @IsOptional()
  @Type(() => Number)
  @IsInt()
  @Min(1)
  categoryId?: number;

  @ApiPropertyOptional({ example: 1 })
  @IsOptional()
  @Type(() => Number)
  @IsInt()
  @Min(1)
  meetingRequestId?: number;

  @ApiPropertyOptional({
    example: 1,
    description: 'ID of the user that created the issue.',
  })
  @IsOptional()
  @Type(() => Number)
  @IsInt()
  @Min(1)
  userId?: number;

  // --- Filters: pivot relation -----------------------------------------

  @ApiPropertyOptional({
    example: 5,
    description: 'Filter to issues that include this agency (any order).',
  })
  @IsOptional()
  @Type(() => Number)
  @IsInt()
  @Min(1)
  stakeholderId?: number;

  @ApiPropertyOptional({
    example: 5,
    description:
      'Filter to issues whose PRIMARY agency (agencyOrder = 1) is this stakeholder.',
  })
  @IsOptional()
  @Type(() => Number)
  @IsInt()
  @Min(1)
  primaryAgencyId?: number;

  // --- Filters: date range ---------------------------------------------

  @ApiPropertyOptional({
    example: '2026-01-01',
    description: 'Inclusive lower bound on createdAt (ISO 8601).',
  })
  @IsOptional()
  @IsDateString()
  createdFrom?: string;

  @ApiPropertyOptional({
    example: '2026-12-31',
    description: 'Inclusive upper bound on createdAt (ISO 8601).',
  })
  @IsOptional()
  @IsDateString()
  createdTo?: string;

  // --- Filters: derived ------------------------------------------------

  @ApiPropertyOptional({
    example: true,
    description: 'true → issues with an attachment, false → issues without.',
  })
  @IsOptional()
  @Transform(toBoolean)
  @IsBoolean()
  hasAttachment?: boolean;
}
