/**
 * Validation and sanitization utilities - pure functions for consistent input validation and cleaning.
 */

import { MAX_IDENTIFIER_LENGTH, MAX_STRING_LENGTH } from './constants.js'
import { DEVICE_TYPES, SOURCE_TYPES } from './deviceUtils.js'

// Constants
export const POI_ICON_TYPES = Object.freeze(['pin', 'flag', 'waypoint'])

// Sanitization functions
const IDENTIFIER_REGEX = /^\w+$/

export function sanitizeString(str, maxLength = MAX_STRING_LENGTH) {
  if (typeof str !== 'string') return ''
  const trimmed = str.trim()
  return trimmed.length > maxLength ? trimmed.slice(0, maxLength) : trimmed
}

export function sanitizeIdentifier(str) {
  if (typeof str !== 'string') return ''
  const trimmed = str.trim()
  if (trimmed.length === 0 || trimmed.length > MAX_IDENTIFIER_LENGTH) return ''
  return IDENTIFIER_REGEX.test(trimmed) ? trimmed : ''
}

export function sanitizeLabel(str, maxLength = MAX_STRING_LENGTH) {
  return sanitizeString(str, maxLength)
}

const ROLES = ['admin', 'leader', 'member']

const validateNumber = (value, field) => {
  const num = Number(value)
  return Number.isFinite(num) ? { valid: true, value: num } : { valid: false, error: `${field} must be a finite number` }
}

const validateEnum = (value, allowed, field) => allowed.includes(value) ? { valid: true, value } : { valid: false, error: `Invalid ${field}` }

const handleField = (d, field, handler, updates, errors, outputField = null) => {
  if (d[field] !== undefined) {
    const result = handler(d[field])
    if (result.valid) updates[outputField || field] = result.value
    else errors.push(result.error)
  }
}

export function validateDevice(data) {
  if (!data || typeof data !== 'object') return { valid: false, errors: ['body required'] }
  const d = /** @type {Record<string, unknown>} */ (data)
  const errors = []
  const latCheck = validateNumber(d.lat, 'lat')
  const lngCheck = validateNumber(d.lng, 'lng')
  if (!latCheck.valid || !lngCheck.valid) errors.push('lat and lng required as finite numbers')
  if (errors.length > 0) return { valid: false, errors }
  return {
    valid: true,
    errors: [],
    data: {
      name: sanitizeString(d.name, 1000),
      device_type: validateEnum(d.device_type, DEVICE_TYPES, 'device_type').value || 'feed',
      vendor: d.vendor !== undefined ? sanitizeString(d.vendor, 255) : null,
      lat: latCheck.value,
      lng: lngCheck.value,
      stream_url: typeof d.stream_url === 'string' ? sanitizeString(d.stream_url, 2000) : '',
      source_type: validateEnum(d.source_type, SOURCE_TYPES, 'source_type').value || 'mjpeg',
      config: d.config == null ? null : (typeof d.config === 'string' ? d.config : JSON.stringify(d.config)),
    },
  }
}

export function validateUpdateDevice(data) {
  if (!data || typeof data !== 'object') return { valid: true, errors: [], data: {} }
  const d = /** @type {Record<string, unknown>} */ (data)
  const errors = []
  const updates = {}
  if (d.name !== undefined) updates.name = sanitizeString(d.name, 1000)
  handleField(d, 'device_type', v => validateEnum(v, DEVICE_TYPES, 'device_type'), updates, errors)
  if (d.vendor !== undefined) updates.vendor = d.vendor === null || d.vendor === '' ? null : sanitizeString(d.vendor, 255)
  handleField(d, 'lat', v => validateNumber(v, 'lat'), updates, errors)
  handleField(d, 'lng', v => validateNumber(v, 'lng'), updates, errors)
  if (d.stream_url !== undefined) updates.stream_url = sanitizeString(d.stream_url, 2000)
  handleField(d, 'source_type', v => validateEnum(v, SOURCE_TYPES, 'source_type'), updates, errors)
  if (d.config !== undefined) updates.config = d.config === null ? null : (typeof d.config === 'string' ? d.config : JSON.stringify(d.config))
  return errors.length > 0 ? { valid: false, errors } : { valid: true, errors: [], data: updates }
}

export function validateUser(data) {
  if (!data || typeof data !== 'object') return { valid: false, errors: ['body required'] }
  const d = /** @type {Record<string, unknown>} */ (data)
  const errors = []
  const identifier = sanitizeIdentifier(d.identifier)
  const password = typeof d.password === 'string' ? d.password : ''
  const role = typeof d.role === 'string' ? d.role : ''
  if (!identifier) errors.push('identifier required')
  if (!password) errors.push('password required')
  if (!role || !ROLES.includes(role)) errors.push('role must be admin, leader, or member')
  return errors.length > 0 ? { valid: false, errors } : { valid: true, errors: [], data: { identifier, password, role: role || 'member' } }
}

export function validateUpdateUser(data) {
  if (!data || typeof data !== 'object') return { valid: true, errors: [], data: {} }
  const d = /** @type {Record<string, unknown>} */ (data)
  const errors = []
  const updates = {}
  if (d.role !== undefined) {
    if (ROLES.includes(d.role)) updates.role = d.role
    else errors.push('role must be admin, leader, or member')
  }
  if (d.identifier !== undefined) {
    const identifier = sanitizeIdentifier(d.identifier)
    if (!identifier) errors.push('identifier cannot be empty')
    else updates.identifier = identifier
  }
  if (d.password !== undefined && d.password !== '') {
    if (typeof d.password !== 'string' || !d.password) errors.push('password cannot be empty')
    else updates.password = d.password
  }
  return errors.length > 0 ? { valid: false, errors } : { valid: true, errors: [], data: updates }
}

export function validatePoi(data) {
  if (!data || typeof data !== 'object') return { valid: false, errors: ['body required'] }
  const d = /** @type {Record<string, unknown>} */ (data)
  const latCheck = validateNumber(d.lat, 'lat')
  const lngCheck = validateNumber(d.lng, 'lng')
  if (!latCheck.valid || !lngCheck.valid) return { valid: false, errors: ['lat and lng required as finite numbers'] }
  return {
    valid: true,
    errors: [],
    data: {
      lat: latCheck.value,
      lng: lngCheck.value,
      label: sanitizeLabel(d.label, 500),
      icon_type: validateEnum(d.iconType, POI_ICON_TYPES, 'iconType').value || 'pin',
    },
  }
}

export function validateUpdatePoi(data) {
  if (!data || typeof data !== 'object') return { valid: true, errors: [], data: {} }
  const d = /** @type {Record<string, unknown>} */ (data)
  const errors = []
  const updates = {}
  if (d.label !== undefined) updates.label = sanitizeLabel(d.label, 500)
  handleField(d, 'iconType', v => validateEnum(v, POI_ICON_TYPES, 'iconType'), updates, errors, 'icon_type')
  handleField(d, 'lat', v => validateNumber(v, 'lat'), updates, errors)
  handleField(d, 'lng', v => validateNumber(v, 'lng'), updates, errors)
  return errors.length > 0 ? { valid: false, errors } : { valid: true, errors: [], data: updates }
}
