import { z } from 'zod'

// ============================================================================
// Filtering Types
// ============================================================================

export type NumberFilter = {
  values?: (number | string)[]
  min?: number | null
  max?: number | null
}
export type DateFilter = {
  values?: string[]
  start?: string | null
  end?: string | null
}
export type StringFilter<T = string> = {
  values?: T[]
  contains?: string | null
  startsWith?: string | null
  endsWith?: string | null
  exclude?: string | null
}

export const zNumberFilter = z.object({
  values: z.array(z.union([z.number(), z.string()])).optional(),
  min: z.number().nullable().optional(),
  max: z.number().nullable().optional(),
})

export const zDateFilter = z.object({
  values: z.array(z.string()).optional(),
  start: z.string().nullable().optional(),
  end: z.string().nullable().optional(),
})

export const zStringFilter = z.object({
  values: z.array(z.string()).optional(),
  contains: z.string().nullable().optional(),
  startsWith: z.string().nullable().optional(),
  endsWith: z.string().nullable().optional(),
  exclude: z.string().nullable().optional(),
})

export type FieldValueType = 'string' | 'number' | 'date' | 'boolean'

export type FilterValue = string[] | boolean[] | NumberFilter | DateFilter | StringFilter | undefined

export type FilterFieldName = string & { __brand?: 'FilterFieldName' }

export type FilterState = Record<FilterFieldName, FilterValue>

/**
 * Wrapper type that adds globalSearch capability to any filter type.
 * Global search is handled separately from field-specific filters.
 */
export type WithGlobalSearch<T> = T & { globalSearch?: string }

/**
 * Zod helper for extending filter schemas with globalSearch.
 */
export const zGlobalSearch = z.object({ globalSearch: z.string().optional() })

type BaseFilterConfig<TFilters extends FilterState> = {
  filterKey: keyof TFilters
  valueType: FieldValueType
  /** Gets the display label for a value. If omitted, String(value) is used. For relation fields, pass referencedItems to resolve ID to label. */
  getLabel?: (value: unknown, referencedItems?: Map<string, any>) => string
}

export type RelationFilterConfig<TFilters extends FilterState> = BaseFilterConfig<TFilters> & {
  kind: 'relation'
  referencedModelViewServiceVariableName: string
  referencedModelLabelField: string
  referencedModelIdField: string
}

type EnumFilterConfig<TFilters extends FilterState> = BaseFilterConfig<TFilters> & {
  kind: 'enum'
  enumName: string
  enumValues: string[]
}

type DiscriminatedUnionFilterConfig<TFilters extends FilterState> = BaseFilterConfig<TFilters> & {
  kind: 'discriminatedUnion'
  duMemberTypes: { type: string; label: string }[]
}

type ScalarFilterConfig<TFilters extends FilterState> = BaseFilterConfig<TFilters> & {
  kind: 'scalar' | 'id'
}

export type FilterConfigItem<TFilters extends FilterState> =
  | RelationFilterConfig<TFilters>
  | EnumFilterConfig<TFilters>
  | DiscriminatedUnionFilterConfig<TFilters>
  | ScalarFilterConfig<TFilters>

export type FilterConfig<TField extends FilterFieldName, TFilters extends FilterState> = Record<
  TField,
  FilterConfigItem<TFilters>
>

// ============================================================================
// Sorting Types
// ============================================================================

/**
 * Sort direction for a single field.
 */
export type SortDirection = 'asc' | 'desc'

/**
 * Zod decoder for sort direction.
 */
export const zSortDirection = z.enum(['asc', 'desc'])

/**
 * A single sort item specifying which field to sort by and in which direction.
 */
export type SortItem<TField extends string = string> = {
  field: TField
  direction: SortDirection
}

/**
 * Sort state is an array of sort items for multi-column sorting.
 * Items are applied in order (primary sort, secondary sort, etc.).
 */
export type SortState<TField extends string = string> = SortItem<TField>[]

// ============================================================================
// Pagination Types
// ============================================================================

/**
 * Result type for paginated queries.
 * Used by getFilteredPaginated and consumed by tRPC infinite queries.
 */
export type PaginatedResult<T> = {
  /** The items for the current page */
  data: T[]
  /** Total count of ALL matching items (not just this page) */
  total: number
  /** Current page number (1-indexed) */
  currentPage: number
  /** Items per page */
  perPage: number
  /** Next page cursor, or null if no more pages */
  cursor: number | null
}
