/**
 * Type Validation Utilities for Edge Functions
 * 
 * Provides runtime type checking to catch mismatches early
 * Generates clear error messages for debugging
 */

import { edgeLogger } from './logger.ts';

/**
 * Validation error with structured details
 */
export class ValidationError extends Error {
  constructor(
    message: string,
    public readonly field: string,
    public readonly expected: string,
    public readonly received: unknown,
    public readonly context?: Record<string, unknown>
  ) {
    super(message);
    this.name = 'ValidationError';
  }
}

/**
 * Valid entity types in the system
 */
export const VALID_ENTITY_TYPES = [
  'park',
  'ride',
  'manufacturer',
  'operator',
  'property_owner',
  'designer',
  'company', // Consolidated type
  'ride_model',
  'photo',
  'milestone',
  'timeline_event',
] as const;

export type ValidEntityType = typeof VALID_ENTITY_TYPES[number];

/**
 * Valid action types
 */
export const VALID_ACTION_TYPES = ['create', 'edit', 'update', 'delete'] as const;
export type ValidActionType = typeof VALID_ACTION_TYPES[number];

/**
 * Type guards
 */

export function isString(value: unknown): value is string {
  return typeof value === 'string';
}

export function isUUID(value: unknown): value is string {
  if (!isString(value)) return false;
  const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
  return uuidRegex.test(value);
}

export function isArray<T>(value: unknown): value is T[] {
  return Array.isArray(value);
}

export function isObject(value: unknown): value is Record<string, unknown> {
  return typeof value === 'object' && value !== null && !Array.isArray(value);
}

export function isValidEntityType(value: unknown): value is ValidEntityType {
  return isString(value) && (VALID_ENTITY_TYPES as readonly string[]).includes(value);
}

export function isValidActionType(value: unknown): value is ValidActionType {
  return isString(value) && (VALID_ACTION_TYPES as readonly string[]).includes(value);
}

/**
 * Validation functions that throw on error
 */

export function validateRequired(
  value: unknown,
  fieldName: string,
  context?: Record<string, unknown>
): void {
  if (value === null || value === undefined || value === '') {
    throw new ValidationError(
      `Missing required field: ${fieldName}`,
      fieldName,
      'non-empty value',
      value,
      context
    );
  }
}

export function validateString(
  value: unknown,
  fieldName: string,
  context?: Record<string, unknown>
): asserts value is string {
  validateRequired(value, fieldName, context);
  if (!isString(value)) {
    throw new ValidationError(
      `Invalid type for ${fieldName}: expected string`,
      fieldName,
      'string',
      typeof value,
      context
    );
  }
}

export function validateUUID(
  value: unknown,
  fieldName: string,
  context?: Record<string, unknown>
): asserts value is string {
  validateString(value, fieldName, context);
  if (!isUUID(value)) {
    throw new ValidationError(
      `Invalid UUID format for ${fieldName}`,
      fieldName,
      'valid UUID',
      value,
      context
    );
  }
}

export function validateArray<T>(
  value: unknown,
  fieldName: string,
  minLength = 0,
  context?: Record<string, unknown>
): asserts value is T[] {
  validateRequired(value, fieldName, context);
  if (!isArray<T>(value)) {
    throw new ValidationError(
      `Invalid type for ${fieldName}: expected array`,
      fieldName,
      'array',
      typeof value,
      context
    );
  }
  if (value.length < minLength) {
    throw new ValidationError(
      `${fieldName} must have at least ${minLength} items`,
      fieldName,
      `array with ${minLength}+ items`,
      `array with ${value.length} items`,
      context
    );
  }
}

export function validateUUIDArray(
  value: unknown,
  fieldName: string,
  minLength = 0,
  context?: Record<string, unknown>
): asserts value is string[] {
  validateArray<string>(value, fieldName, minLength, context);
  
  for (let i = 0; i < value.length; i++) {
    if (!isUUID(value[i])) {
      throw new ValidationError(
        `Invalid UUID at index ${i} in ${fieldName}`,
        `${fieldName}[${i}]`,
        'valid UUID',
        value[i],
        context
      );
    }
  }
}

export function validateEntityType(
  value: unknown,
  fieldName: string,
  context?: Record<string, unknown>
): asserts value is ValidEntityType {
  validateString(value, fieldName, context);
  if (!isValidEntityType(value)) {
    throw new ValidationError(
      `Invalid entity type: ${value}. Must be one of: ${VALID_ENTITY_TYPES.join(', ')}`,
      fieldName,
      `one of: ${VALID_ENTITY_TYPES.join(', ')}`,
      value,
      { ...context, validTypes: VALID_ENTITY_TYPES }
    );
  }
}

export function validateActionType(
  value: unknown,
  fieldName: string,
  context?: Record<string, unknown>
): asserts value is ValidActionType {
  validateString(value, fieldName, context);
  if (!isValidActionType(value)) {
    throw new ValidationError(
      `Invalid action type: ${value}. Must be one of: ${VALID_ACTION_TYPES.join(', ')}`,
      fieldName,
      `one of: ${VALID_ACTION_TYPES.join(', ')}`,
      value,
      { ...context, validActions: VALID_ACTION_TYPES }
    );
  }
}

export function validateObject(
  value: unknown,
  fieldName: string,
  context?: Record<string, unknown>
): asserts value is Record<string, unknown> {
  validateRequired(value, fieldName, context);
  if (!isObject(value)) {
    throw new ValidationError(
      `Invalid type for ${fieldName}: expected object`,
      fieldName,
      'object',
      typeof value,
      context
    );
  }
}

/**
 * Validate submission item data structure
 */
export interface SubmissionItemValidation {
  id: string;
  item_type: ValidEntityType;
  action_type: ValidActionType;
  item_data?: Record<string, unknown>;
}

export function validateSubmissionItem(
  item: unknown,
  context?: Record<string, unknown>
): SubmissionItemValidation {
  validateObject(item, 'submission_item', context);
  
  const obj = item as Record<string, unknown>;
  
  // Validate ID
  validateUUID(obj.id, 'submission_item.id', { ...context, item });
  
  // Validate item_type
  validateEntityType(obj.item_type, 'submission_item.item_type', { 
    ...context, 
    item,
    itemId: obj.id 
  });
  
  // Validate action_type
  validateActionType(obj.action_type, 'submission_item.action_type', { 
    ...context, 
    item,
    itemId: obj.id 
  });
  
  return {
    id: obj.id,
    item_type: obj.item_type,
    action_type: obj.action_type,
    item_data: isObject(obj.item_data) ? obj.item_data : undefined,
  };
}

/**
 * Log validation error for monitoring
 */
export function logValidationError(
  error: ValidationError,
  requestId?: string,
  action?: string
): void {
  edgeLogger.error('Validation error', {
    requestId,
    action,
    errorType: 'ValidationError',
    field: error.field,
    expected: error.expected,
    received: error.received,
    message: error.message,
    context: error.context,
  });
}

/**
 * Validate request body structure
 */
export async function parseAndValidateJSON<T = unknown>(
  req: Request,
  schema: (data: unknown) => T,
  context?: Record<string, unknown>
): Promise<T> {
  let body: unknown;
  
  try {
    body = await req.json();
  } catch (error) {
    throw new ValidationError(
      'Invalid JSON in request body',
      'request.body',
      'valid JSON',
      error instanceof Error ? error.message : String(error),
      context
    );
  }
  
  try {
    return schema(body);
  } catch (error) {
    if (error instanceof ValidationError) {
      throw error;
    }
    throw new ValidationError(
      'Request body validation failed',
      'request.body',
      'valid request structure',
      body,
      { ...context, originalError: error }
    );
  }
}
