import { z } from 'zod'

/**
 * Creates a Zod decoder for parsing a comma-separated fields query parameter.
 * Only allows field names that exist in the provided schema.
 *
 * @param schema - A Zod object schema to extract valid field names from
 * @returns A Zod decoder that validates and parses the fields parameter
 *
 * @example
 * const mySchema = z.object({ id: z.string(), name: z.string() })
 * const fieldsDecoder = createFieldsDecoder(mySchema)
 * // ?fields=id,name -> ['id', 'name']
 * // ?fields=id,invalid -> throws validation error
 */
export function createFieldsDecoder<T extends z.ZodRawShape>(schema: z.ZodObject<T>) {
  const validFields = Object.keys(schema.shape) as (keyof T)[]

  return z
    .string()
    .transform((value, ctx) => {
      if (!value || value.trim() === '') {
        return undefined
      }

      const fields = value
        .split(',')
        .map((f) => f.trim())
        .filter((f) => f.length > 0)

      // Validate that all fields are keys of the schema
      for (const field of fields) {
        if (!validFields.includes(field as keyof T)) {
          ctx.addIssue({
            code: 'custom',
            message: `Invalid field "${field}". Valid fields are: ${validFields.join(', ')}`,
          })
          return z.NEVER
        }
      }

      return fields as (keyof T & string)[]
    })
    .optional()
    .describe('Comma-separated list of fields to include in the response')
}

export type FieldSelection<T> = (keyof T & string)[] | undefined

/**
 * Picks only the specified fields from an object.
 * If no fields are specified, returns the original object.
 */
export function selectFields<T extends Record<string, unknown>>(data: T, fields: FieldSelection<T>): Partial<T> {
  if (!fields || fields.length === 0) {
    return data
  }

  const result: Partial<T> = {}
  for (const field of fields) {
    if (field in data) {
      result[field as keyof T] = data[field as keyof T]
    }
  }
  return result
}
