/** `@platform-modules/forms` — shared contract types. Validator-agnostic (no Zod baked). */

export type Result<T, E> = { ok: true; value: T } | { ok: false; error: E }

export type FieldType =
  | 'text'
  | 'email'
  | 'url'
  | 'tel'
  | 'number'
  | 'textarea'
  | 'select'
  | 'multiselect'
  | 'checkbox'
  | 'radio'
  | 'date'
  | 'hidden'

export interface FieldDef {
  name: string
  type: FieldType
  label: string
  required?: boolean
  options?: Array<{ value: string; label: string }>
  // validator-AGNOSTIC structural constraints (NOT Zod-coupled):
  min?: number // length (text) / value (number) / count (multiselect)
  max?: number
  pattern?: string // RegExp source; tested against the string value
  default?: unknown
}

export interface ChallengeAdapter {
  verify(token: string, ctx: { ip?: string }): Promise<boolean>
}

export interface AntiSpamConfig {
  honeypot?: string // a field name that MUST stay empty (bots fill it)
  minFillMs?: number // submit faster than this since render = bot (time-trap)
  challenge?: ChallengeAdapter // pluggable captcha/turnstile — behind a seam, never bundled
}

export interface FormDef {
  id: string // stable, kebab-case
  fields: FieldDef[]
  antispam?: AntiSpamConfig
  meta?: { name?: string; description?: string }
}

export type SubmissionValue = string | string[] | number | boolean | null
export type CleanSubmission = Record<string, SubmissionValue>

export interface StoredSubmission {
  id: string
  formId: string
  data: CleanSubmission
  createdAt: string // ISO
  meta?: { ip?: string; userAgent?: string; ref?: string }
  spam?: boolean // flagged-not-dropped (review queue)
}

export type FormErrorCode =
  | 'empty-id'
  | 'no-fields'
  | 'duplicate-field'
  | 'missing-name'
  | 'missing-label'
  | 'no-options'
  | 'bad-pattern'

export interface FormError {
  code: FormErrorCode
  field?: string
  message: string
}

export type FieldErrorCode = 'required' | 'type' | 'min' | 'max' | 'pattern' | 'option'

export interface FieldError {
  field: string
  code: FieldErrorCode
  message: string
}

export interface AntiSpamVerdict {
  ok: boolean
  reason?: 'honeypot' | 'too-fast' | 'bad-token' | 'challenge-failed'
}

/**
 * Structural type-guard for a {@link FieldError} — cross-package safe (no `instanceof`;
 * two deduped copies of the package would break identity-by-class).
 */
export function isFieldError(e: unknown): e is FieldError {
  if (typeof e !== 'object' || e === null) return false
  const o = e as Record<string, unknown>
  return typeof o.field === 'string' && typeof o.code === 'string' && typeof o.message === 'string'
}
