import { CommentValidationError } from './errors.js'
import type { CommentAuthor, CommentInput, CommentTarget } from './types.js'

/** Maximum raw body length (cheap-DoS floor, spec §3). */
export const MAX_BODY_LEN = 10_000

function validateTarget(target: unknown): asserts target is CommentTarget {
  if (
    typeof target !== 'object' ||
    target === null ||
    typeof (target as CommentTarget).type !== 'string' ||
    !(target as CommentTarget).type.trim() ||
    typeof (target as CommentTarget).id !== 'string' ||
    !(target as CommentTarget).id.trim()
  ) {
    throw new CommentValidationError('target', 'must have non-empty string type and id')
  }
}

function validateAuthor(author: unknown): asserts author is CommentAuthor {
  if (typeof author !== 'object' || author === null) {
    throw new CommentValidationError('author', 'must be an object')
  }
  const a = author as Record<string, unknown>
  if (a.kind === 'user') {
    if (typeof a.userId !== 'string' || !String(a.userId).trim()) {
      throw new CommentValidationError('author.userId', 'required for user kind')
    }
  } else if (a.kind === 'guest') {
    if (typeof a.name !== 'string' || !String(a.name).trim()) {
      throw new CommentValidationError('author.name', 'required for guest kind')
    }
  } else {
    throw new CommentValidationError('author.kind', 'must be "user" or "guest"')
  }
}

/** Normalized comment input — parentId always explicit null when absent. */
export type NormalizedCommentInput = CommentInput & { parentId: string | null }

/**
 * Validator-agnostic boundary normalization (spec §3, coding-standard).
 * Throws CommentValidationError for: empty body, body > MAX_BODY_LEN, bad target, bad author.
 */
export function normalizeCommentInput(raw: CommentInput): NormalizedCommentInput {
  if (typeof raw !== 'object' || raw === null) {
    throw new CommentValidationError('input', 'must be an object')
  }

  validateTarget(raw.target)

  const body = typeof raw.body === 'string' ? raw.body : ''
  const trimmedBody = body.trim()
  if (!trimmedBody) {
    throw new CommentValidationError('body', 'must not be empty')
  }
  if (body.length > MAX_BODY_LEN) {
    throw new CommentValidationError('body', `exceeds maximum length of ${MAX_BODY_LEN} characters`)
  }

  validateAuthor(raw.author)

  return {
    target: raw.target,
    parentId: raw.parentId ?? null,
    author: raw.author,
    body,
    initialStatus: raw.initialStatus,
  }
}
