/**
 * Type Conversion Utilities
 * 
 * This file provides utilities to convert between TypeScript and Python/API formats,
 * ensuring data consistency between frontend and backend.
 */

// ============================
// Generic Conversion Functions
// ============================

/**
 * Converts a camelCase object to snake_case for sending to the Python backend
 */
export function toSnakeCase<T extends Record<string, any>>(obj: T): Record<string, any> {
  if (!obj || typeof obj !== 'object') return obj;
  
  if (Array.isArray(obj)) {
    return obj.map(item => toSnakeCase(item));
  }
  
  const result: Record<string, any> = {};
  
  for (const [key, value] of Object.entries(obj)) {
    const snakeCaseKey = camelToSnake(key);
    
    if (value === null || value === undefined) {
      result[snakeCaseKey] = value;
    } else if (Array.isArray(value)) {
      result[snakeCaseKey] = value.map(item => 
        typeof item === 'object' && item !== null ? toSnakeCase(item) : item
      );
    } else if (value instanceof Date) {
      // Convert Date to ISO string for Python datetime
      result[snakeCaseKey] = value.toISOString();
    } else if (typeof value === 'object') {
      result[snakeCaseKey] = toSnakeCase(value);
    } else {
      result[snakeCaseKey] = value;
    }
  }
  
  return result;
}

/**
 * Converts a snake_case object to camelCase for TypeScript/JavaScript
 */
export function toCamelCase<T>(obj: Record<string, any>): T {
  if (!obj || typeof obj !== 'object') return obj as T;
  
  if (Array.isArray(obj)) {
    return obj.map(item => toCamelCase(item)) as T;
  }
  
  const result: Record<string, any> = {};
  
  for (const [key, value] of Object.entries(obj)) {
    const camelCaseKey = snakeToCamel(key);
    
    if (value === null || value === undefined) {
      result[camelCaseKey] = value;
    } else if (Array.isArray(value)) {
      result[camelCaseKey] = value.map(item => 
        typeof item === 'object' && item !== null ? toCamelCase(item) : item
      );
    } else if (typeof value === 'string' && isIsoDateString(value)) {
      // Convert ISO date string to Date object
      result[camelCaseKey] = new Date(value);
    } else if (typeof value === 'object') {
      result[camelCaseKey] = toCamelCase(value);
    } else {
      result[camelCaseKey] = value;
    }
  }
  
  return result as T;
}

/**
 * Helper function to convert camelCase to snake_case
 */
function camelToSnake(str: string): string {
  return str.replace(/[A-Z]/g, letter => `_${letter.toLowerCase()}`);
}

/**
 * Helper function to convert snake_case to camelCase
 */
function snakeToCamel(str: string): string {
  return str.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase());
}

/**
 * Checks if a string is an ISO date format
 */
function isIsoDateString(value: string): boolean {
  if (typeof value !== 'string') return false;
  return /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})?$/.test(value);
}

// ============================
// API Request/Response Formatting
// ============================

/**
 * Format data for API requests (converts to format expected by Python backend)
 */
export function formatApiRequest<T extends Record<string, any>>(data: T): Record<string, any> {
  if (!data) return data;
  
  // Create a new object to avoid mutating the original
  const formatted: Record<string, any> = {};
  
  // Convert dates to ISO strings and handle nested objects
  for (const [key, value] of Object.entries(data)) {
    if (value instanceof Date) {
      formatted[key] = value.toISOString();
    } else if (Array.isArray(value)) {
      formatted[key] = value.map(item => {
        if (item instanceof Date) {
          return item.toISOString();
        } else if (typeof item === 'object' && item !== null) {
          return formatApiRequest(item);
        }
        return item;
      });
    } else if (typeof value === 'object' && value !== null) {
      formatted[key] = formatApiRequest(value);
    } else {
      formatted[key] = value;
    }
  }
  
  return toSnakeCase(formatted);
}

/**
 * Parse API responses and convert to TypeScript format
 */
export function parseApiResponse<T>(data: any): ApiResponse<T> {
  if (!data || typeof data !== 'object') {
    return {
      success: false,
      error: {
        code: 'INVALID_RESPONSE',
        message: 'Invalid response format'
      }
    };
  }
  
  // Convert any snake_case fields to camelCase and parse dates
  const parsed = toCamelCase<ApiResponse<T>>(data);
  
  return parsed;
}

/**
 * Parse paginated API responses
 */
export function parsePaginatedResponse<T>(
  data: any, 
  itemParser?: (item: any) => T
): ApiResponse<PaginatedResponse<T>> {
  const apiResponse = parseApiResponse<PaginatedResponse<any>>(data);
  
  if (!apiResponse.success || !apiResponse.data) {
    return apiResponse as ApiResponse<PaginatedResponse<T>>;
  }
  
  const paginatedData = apiResponse.data;
  
  // Apply item parser if provided
  if (itemParser && Array.isArray(paginatedData.data)) {
    return {
      ...apiResponse,
      data: {
        ...paginatedData,
        data: paginatedData.data.map(itemParser)
      }
    };
  }
  
  return apiResponse as ApiResponse<PaginatedResponse<T>>;
}

// ============================
// URL Parameter Formatting
// ============================

/**
 * Convert object to URL search parameters
 */
export function toUrlParams(obj: Record<string, any>): URLSearchParams {
  const params = new URLSearchParams();
  
  for (const [key, value] of Object.entries(obj)) {
    if (value !== null && value !== undefined) {
      if (Array.isArray(value)) {
        // Handle arrays by adding multiple params with same key
        value.forEach(item => {
          params.append(key, String(item));
        });
      } else if (value instanceof Date) {
        params.append(key, value.toISOString());
      } else if (typeof value === 'object') {
        // For nested objects, we could flatten or JSON stringify
        params.append(key, JSON.stringify(value));
      } else {
        params.append(key, String(value));
      }
    }
  }
  
  return params;
}

// ============================
// Validation Helpers
// ============================

/**
 * Check if response is a successful API response
 */
export function isSuccessResponse<T>(response: any): response is SuccessApiResponse<T> {
  return response && typeof response === 'object' && response.success === true;
}

/**
 * Check if response is an error API response
 */
export function isErrorResponse(response: any): response is ErrorApiResponse {
  return response && typeof response === 'object' && response.success === false;
}

/**
 * Extract data from API response or throw error
 */
export function extractApiData<T>(response: ApiResponse<T>): T {
  if (isSuccessResponse(response) && response.data !== undefined) {
    return response.data;
  }
  
  const errorMessage = isErrorResponse(response) 
    ? response.error?.message || 'Unknown API error'
    : 'Invalid API response format';
    
  throw new Error(errorMessage);
}

// ============================
// Type Definitions (matching generated types)
// ============================

export interface ApiResponse<T> {
  success: boolean;
  data?: T;
  error?: {
    code: string;
    message: string;
    details?: any;
  };
  meta?: Record<string, any>;
}

export interface SuccessApiResponse<T> extends ApiResponse<T> {
  success: true;
  data: T;
}

export interface ErrorApiResponse extends ApiResponse<never> {
  success: false;
  error: {
    code: string;
    message: string;
    details?: any;
  };
}

export interface PaginatedResponse<T> {
  data: T[];
  total: number;
  page: number;
  limit: number;
  totalPages: number;
  hasMore: boolean;
}

export interface PaginatedRequest {
  page?: number;
  limit?: number;
  sortBy?: string;
  sortOrder?: 'asc' | 'desc';
  filters?: Record<string, any>;
}

// ============================
// Convenience Functions for Common Operations
// ============================

/**
 * Create a paginated request with defaults
 */
export function createPaginatedRequest(params: Partial<PaginatedRequest> = {}): PaginatedRequest {
  return {
    page: 1,
    limit: 20,
    sortOrder: 'desc',
    ...params
  };
}

/**
 * Handle API response with automatic error throwing
 */
export async function handleApiResponse<T>(response: Response): Promise<T> {
  if (!response.ok) {
    const errorText = await response.text();
    throw new Error(`HTTP ${response.status}: ${errorText}`);
  }
  
  const data = await response.json();
  const apiResponse = parseApiResponse<T>(data);
  
  return extractApiData(apiResponse);
}

/**
 * Handle paginated API response
 */
export async function handlePaginatedApiResponse<T>(
  response: Response,
  itemParser?: (item: any) => T
): Promise<PaginatedResponse<T>> {
  if (!response.ok) {
    const errorText = await response.text();
    throw new Error(`HTTP ${response.status}: ${errorText}`);
  }
  
  const data = await response.json();
  const apiResponse = parsePaginatedResponse<T>(data, itemParser);
  
  return extractApiData(apiResponse);
}

// ============================
// Debug Utilities
// ============================

/**
 * Log conversion for debugging
 */
export function debugConversion<T>(obj: T, label: string = 'Object'): T {
  if (process.env.NODE_ENV === 'development') {
    console.group(`🔄 ${label} Conversion`);
    console.log('Original:', obj);
    if (typeof obj === 'object' && obj !== null) {
      console.log('Formatted for API:', formatApiRequest(obj as any));
    }
    console.groupEnd();
  }
  return obj;
}

const exports = {
  toSnakeCase,
  toCamelCase,
  formatApiRequest,
  parseApiResponse,
  parsePaginatedResponse,
  toUrlParams,
  isSuccessResponse,
  isErrorResponse,
  extractApiData,
  createPaginatedRequest,
  handleApiResponse,
  handlePaginatedApiResponse,
  debugConversion
}

export default exports;