/**
 * Validation utilities and helpers
 */

import { z } from 'zod';
import { JWT_ID_PATTERN, ERROR_CODES } from './constants';
import { PollingError } from './types';
import { 
  StarredProjectsResponseSchema, 
  DeepLinkParamsSchema,
  ErrorResponseSchema,
  RateLimitResponseSchema 
} from './schemas';

/**
 * Validate JWT ID format
 */
export function validateJwtId(jwtId: string): boolean {
  return JWT_ID_PATTERN.test(jwtId);
}

/**
 * Compare JWT IDs lexicographically
 */
export function compareJwtIds(a: string, b: string): number {
  if (!validateJwtId(a) || !validateJwtId(b)) {
    throw new Error('Invalid JWT ID format');
  }
  return a.localeCompare(b);
}

/**
 * Extract timestamp from JWT ID
 */
export function extractJwtTimestamp(jwtId: string): number {
  const match = jwtId.match(JWT_ID_PATTERN);
  if (!match || !match[1]) {
    throw new Error('Invalid JWT ID format');
  }
  return parseInt(match[1], 10);
}

/**
 * Validate starred projects response
 */
export function validateStarredProjectsResponse(data: unknown): boolean {
  return StarredProjectsResponseSchema.safeParse(data).success;
}

/**
 * Validate deep link parameters
 */
export function validateDeepLinkParams(params: unknown): boolean {
  return DeepLinkParamsSchema.safeParse(params).success;
}

/**
 * Validate error response
 */
export function validateErrorResponse(data: unknown): boolean {
  return ErrorResponseSchema.safeParse(data).success;
}

/**
 * Validate rate limit response
 */
export function validateRateLimitResponse(data: unknown): boolean {
  return RateLimitResponseSchema.safeParse(data).success;
}

/**
 * Create response schema validator
 */
export function createResponseValidator<T>(schema: z.ZodSchema<T>): {
  validate: (data: unknown) => data is T;
  transformError: (error: unknown) => PollingError;
} {
  return {
    validate: (data: unknown): data is T => schema.safeParse(data).success,
    transformError: (error: unknown): PollingError => ({
      code: ERROR_CODES.VALIDATION_ERROR,
      message: (error as Error).message || 'Validation failed',
      retryable: false
    })
  };
}

/**
 * Safe parse with error transformation
 */
export function safeParseWithError<T>(
  schema: z.ZodSchema<T>, 
  data: unknown
): { success: true; data: T } | { success: false; error: string } {
  const result = schema.safeParse(data);
  
  if (result.success) {
    return { success: true, data: result.data };
  }
  
  return { 
    success: false, 
    error: result.error.errors.map(e => `${e.path.join('.')}: ${e.message}`).join(', ')
  };
}

/**
 * Validate idempotency key format
 */
export function validateIdempotencyKey(key: string): boolean {
  // UUID v4 format
  const uuidV4Pattern = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
  return uuidV4Pattern.test(key);
}

/**
 * Generate idempotency key
 */
export function generateIdempotencyKey(): string {
  return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
    const r = Math.random() * 16 | 0;
    const v = c === 'x' ? r : (r & 0x3 | 0x8);
    return v.toString(16);
  });
}

/**
 * Hash DID for privacy in logs
 */
export function hashDid(did: string): string {
  // Simple hash for privacy (use crypto in production)
  const hash = did.split('').reduce((a, b) => {
    a = ((a << 5) - a) + b.charCodeAt(0);
    return a & a;
  }, 0);
  return `did:hash:${Math.abs(hash).toString(16)}`;
}

/**
 * Redact PII from logs
 */
export function redactPii(data: unknown): unknown {
  const redacted = JSON.parse(JSON.stringify(data));
  
  // Redact DID patterns
  if (typeof redacted === 'string') {
    return redacted.replace(/did:key:[a-zA-Z0-9]+/g, (match) => hashDid(match));
  }
  
  if (typeof redacted === 'object' && redacted !== null) {
    for (const key in redacted) {
      if (typeof redacted[key] === 'string') {
        redacted[key] = redacted[key].replace(/did:key:[a-zA-Z0-9]+/g, (match: string) => hashDid(match));
      } else if (typeof redacted[key] === 'object') {
        redacted[key] = redactPii(redacted[key]);
      }
    }
  }
  
  return redacted;
}
