/**
 * API Response Schemas
 * FE-TYPE-002: Create Zod schemas to validate API responses at runtime
 * 
 * Provides Zod schemas for all API response types to ensure type safety
 * and runtime validation of API responses.
 */

import { z } from 'zod';
import { normalizeObjectIds } from '@/utils/idNormalization';

/**
 * Base UUID schema
 */
export const uuidSchema = z.string().uuid('Invalid UUID format');

/**
 * ISO8601 date string schema
 */
export const isoDateSchema = z.string().datetime({ message: 'Invalid ISO8601 date format' });

/**
 * User schema
 */
export const userSchema = z.object({
  id: uuidSchema,
  username: z.string().min(1),
  slug: z.string().optional(),
  email: z.string().email(),
  first_name: z.string().optional().nullable(),
  last_name: z.string().optional().nullable(),
  avatar: z.string().optional().nullable(),
  bio: z.string().optional().nullable(),
  location: z.string().optional().nullable(),
  birthdate: isoDateSchema.optional().nullable(),
  gender: z.string().optional().nullable(),
  username_changed_at: isoDateSchema.optional().nullable(),
  role: z.enum(['user', 'admin', 'super_admin']),
  is_active: z.boolean(),
  is_verified: z.boolean(),
  is_admin: z.boolean(),
  is_public: z.boolean(),
  last_login_at: isoDateSchema.optional().nullable(),
  created_at: isoDateSchema,
  updated_at: isoDateSchema,
  is_2fa_enabled: z.boolean().optional(),
});

export type User = z.infer<typeof userSchema>;

/**
 * Message schema
 */
export const messageSchema = z.object({
  id: uuidSchema,
  conversation_id: uuidSchema,
  sender_id: uuidSchema,
  content: z.string(),
  message_type: z.enum(['text', 'image', 'audio', 'file']),
  attachment_url: z.string().url().optional(),
  created_at: isoDateSchema,
  updated_at: isoDateSchema,
  sender: userSchema.optional(),
});

export type Message = z.infer<typeof messageSchema>;

/**
 * Conversation schema
 */
export const conversationSchema = z.object({
  id: uuidSchema,
  name: z.string(),
  type: z.enum(['direct', 'group']),
  creator_id: uuidSchema,
  created_at: isoDateSchema,
  updated_at: isoDateSchema,
  participants: z.array(userSchema).optional(),
  last_message: messageSchema.optional(),
  unread_count: z.number().int().nonnegative().optional(),
});

export type Conversation = z.infer<typeof conversationSchema>;

/**
 * Track schema
 */
export const trackSchema = z.object({
  id: uuidSchema,
  creator_id: uuidSchema,
  file_id: uuidSchema.optional().nullable(),
  title: z.string().min(1),
  artist: z.string().min(1),
  album: z.string(),
  duration: z.number().nonnegative(),
  genre: z.string(),
  year: z.number().int().min(1900).max(2100),
  file_path: z.string(),
  file_size: z.number().nonnegative(),
  format: z.string(),
  bitrate: z.number().nonnegative(),
  sample_rate: z.number().nonnegative(),
  waveform_path: z.string().optional().nullable(),
  cover_art_path: z.string().optional().nullable(),
  is_public: z.boolean(),
  status: z.enum(['uploading', 'processing', 'completed', 'failed']),
  status_message: z.string().optional().nullable(),
  stream_status: z.enum(['pending', 'processing', 'ready', 'error']),
  stream_manifest_url: z.string().url().optional().nullable(),
  play_count: z.number().int().nonnegative(),
  like_count: z.number().int().nonnegative(),
  created_at: isoDateSchema,
  updated_at: isoDateSchema,
  user: userSchema.optional(),
});

export type Track = z.infer<typeof trackSchema>;

/**
 * Playlist schema
 */
export const playlistSchema = z.object({
  id: uuidSchema,
  user_id: uuidSchema,
  title: z.string().min(1),
  description: z.string().optional().nullable(),
  is_public: z.boolean(),
  cover_url: z.string().url().optional().nullable(),
  track_count: z.number().int().nonnegative(),
  follower_count: z.number().int().nonnegative(),
  created_at: isoDateSchema,
  updated_at: isoDateSchema,
  tracks: z.array(trackSchema).optional(),
  user: userSchema.optional(),
  playlist_tracks: z.array(z.any()).optional(), // Complex nested type
  collaborators: z.array(z.any()).optional(), // Complex nested type
});

export type Playlist = z.infer<typeof playlistSchema>;

/**
 * Session schema
 */
export const sessionSchema = z.object({
  id: uuidSchema,
  user_id: uuidSchema,
  ip_address: z.string(),
  user_agent: z.string(),
  revoked_at: isoDateSchema.optional().nullable(),
  expires_at: isoDateSchema,
  created_at: isoDateSchema,
});

export type Session = z.infer<typeof sessionSchema>;

/**
 * AuditLog schema
 */
export const auditLogSchema = z.object({
  id: uuidSchema,
  user_id: uuidSchema.optional().nullable(),
  action: z.string(),
  resource: z.string(),
  resource_id: uuidSchema.optional().nullable(),
  metadata: z.record(z.any()).optional().nullable(),
  ip_address: z.string().optional().nullable(),
  user_agent: z.string().optional().nullable(),
  timestamp: isoDateSchema,
});

export type AuditLog = z.infer<typeof auditLogSchema>;

/**
 * ApiError schema
 */
export const apiErrorSchema = z.object({
  code: z.number().int(),
  message: z.string(),
  details: z.array(z.object({
    field: z.string(),
    message: z.string(),
  })).optional(),
  request_id: z.string().optional(),
  timestamp: isoDateSchema,
  context: z.record(z.any()).optional(),
  retry_after: z.number().int().positive().optional(),
});

export type ApiError = z.infer<typeof apiErrorSchema>;

/**
 * ApiResponse schema (generic)
 */
export const apiResponseSchema = <T extends z.ZodTypeAny>(dataSchema: T) =>
  z.object({
    success: z.boolean(),
    data: dataSchema.nullable(),
    error: apiErrorSchema.optional(),
    message: z.string().optional(),
  });

/**
 * PaginationData schema
 */
export const paginationDataSchema = z.object({
  page: z.number().int().positive(),
  limit: z.number().int().positive(),
  total: z.number().int().nonnegative(),
  total_pages: z.number().int().nonnegative(),
  has_next: z.boolean(),
  has_prev: z.boolean(),
  next_cursor: z.string().optional(),
  prev_cursor: z.string().optional(),
});

export type PaginationData = z.infer<typeof paginationDataSchema>;

/**
 * ListResponse schema (generic)
 */
export const listResponseSchema = <T extends z.ZodTypeAny>(itemSchema: T) =>
  z.object({
    items: z.array(itemSchema),
    pagination: paginationDataSchema,
  });

/**
 * Notification schema
 */
export const notificationSchema = z.object({
  id: uuidSchema,
  user_id: uuidSchema,
  type: z.enum(['new_message', 'track_uploaded', 'user_mentioned', 'system']),
  content: z.string(),
  read: z.boolean(),
  created_at: isoDateSchema,
});

export type Notification = z.infer<typeof notificationSchema>;

/**
 * PlaylistTrack schema
 */
export const playlistTrackSchema = z.object({
  id: uuidSchema,
  playlist_id: uuidSchema,
  track_id: uuidSchema,
  position: z.number().int().nonnegative(),
  added_by: uuidSchema,
  added_at: isoDateSchema,
  track: trackSchema.optional(),
});

export type PlaylistTrack = z.infer<typeof playlistTrackSchema>;

/**
 * PlaylistCollaborator schema
 */
export const playlistCollaboratorSchema = z.object({
  id: uuidSchema,
  playlist_id: uuidSchema,
  user_id: uuidSchema,
  role: z.enum(['owner', 'editor', 'viewer']),
  created_at: isoDateSchema,
  user: userSchema.optional(),
});

export type PlaylistCollaborator = z.infer<typeof playlistCollaboratorSchema>;

/**
 * Validate and normalize API response
 * 
 * @param schema - Zod schema to validate against
 * @param data - Data to validate
 * @param options - Validation options
 * @returns Validated and normalized data
 * @throws ZodError if validation fails
 */
export function validateApiResponse<T>(
  schema: z.ZodSchema<T>,
  data: unknown,
  options: {
    normalizeIds?: boolean;
  } = {},
): T {
  const { normalizeIds = true } = options;

  // Normalize IDs if requested
  let normalizedData = data;
  if (normalizeIds && typeof data === 'object' && data !== null) {
    normalizedData = normalizeObjectIds(data as Record<string, unknown>);
  }

  // Validate with schema
  // Note: Zod parse() is permissive by default (allows extra fields)
  // Strict mode would require using .strict() on object schemas, but we keep it simple
  return schema.parse(normalizedData);
}

/**
 * Safe validate API response (returns result instead of throwing)
 * 
 * @param schema - Zod schema to validate against
 * @param data - Data to validate
 * @param options - Validation options
 * @returns Validation result
 */
export function safeValidateApiResponse<T>(
  schema: z.ZodSchema<T>,
  data: unknown,
  options: {
    normalizeIds?: boolean;
  } = {},
): {
  success: boolean;
  data?: T;
  error?: z.ZodError;
} {
  try {
    const validated = validateApiResponse(schema, data, options);
    return { success: true, data: validated };
  } catch (error) {
    if (error instanceof z.ZodError) {
      return { success: false, error };
    }
    throw error;
  }
}

/**
 * Validate API response array
 */
export function validateApiResponseArray<T>(
  itemSchema: z.ZodSchema<T>,
  data: unknown,
  options?: {
    normalizeIds?: boolean;
  },
): T[] {
  const arraySchema = z.array(itemSchema);
  const { normalizeIds = true } = options || {};

  // Normalize IDs if requested
  let normalizedData = data;
  if (normalizeIds && Array.isArray(data)) {
    normalizedData = data.map((item) => 
      typeof item === 'object' && item !== null
        ? normalizeObjectIds(item as Record<string, unknown>)
        : item
    );
  }

  // Validate with schema
  return arraySchema.parse(normalizedData);
}

/**
 * Validate paginated API response
 */
export function validatePaginatedResponse<T>(
  itemSchema: z.ZodSchema<T>,
  data: unknown,
  options?: {
    normalizeIds?: boolean;
  },
): {
  items: T[];
  pagination: PaginationData;
} {
  const paginatedSchema = listResponseSchema(itemSchema);
  return validateApiResponse(paginatedSchema, data, options);
}

