import { z } from 'zod'
import { DescriptionSchema, LabelSchema, ModelIdSchema } from './shared'
import { defineDomainRecord } from '../helpers'

export const EntityIdSchema = ModelIdSchema

export const EntityLinkKindSchema = z
  .enum(['belongs-to', 'has-many', 'has-one', 'many-to-many'])
  .meta({ label: 'Link kind' })

export const EntityLinkSchema = z.object({
  toEntity: EntityIdSchema.meta({ ref: 'entity' }),
  kind: EntityLinkKindSchema,
  via: z.string().trim().min(1).max(255).optional(),
  label: LabelSchema.optional()
})

export const EntitySchema = z.object({
  id: EntityIdSchema,
  /** Domain-map iteration order. Convention: multiples of 10 (10, 20, 30, ...) to allow easy insertion. */
  order: z.number(),
  label: LabelSchema,
  description: DescriptionSchema.optional(),
  ownedBySystemId: ModelIdSchema.meta({ ref: 'system' }),
  table: z.string().trim().min(1).max(255).optional(),
  rowSchema: ModelIdSchema.optional(),
  stateCatalogId: ModelIdSchema.optional(),
  links: z.array(EntityLinkSchema).optional()
})

export const EntitiesDomainSchema = z
  .record(z.string(), EntitySchema)
  .refine((record) => Object.entries(record).every(([key, entry]) => entry.id === key), {
    message: 'Each entity entry id must match its map key'
  })
  .default({})

/** Validate and return a single entity entry. */
export function defineEntity(entry: z.input<typeof EntitySchema>): z.infer<typeof EntitySchema> {
  return EntitySchema.parse(entry)
}

/** Validate and return an id-keyed map of entity entries. */
export function defineEntities(
  entries: readonly z.input<typeof EntitySchema>[]
): Record<string, z.infer<typeof EntitySchema>> {
  return defineDomainRecord(EntitySchema, entries)
}

export type EntityId = z.infer<typeof EntityIdSchema>
export type EntityLinkKind = z.infer<typeof EntityLinkKindSchema>
export type EntityLink = z.infer<typeof EntityLinkSchema>
export type Entity = z.infer<typeof EntitySchema>
export type EntitiesDomain = z.infer<typeof EntitiesDomainSchema>
