/**
 * Type Guards
 * FE-TYPE-004: Add type guard functions for safe type narrowing
 *
 * Provides runtime type checking functions that allow TypeScript to narrow
 * types safely, improving type safety throughout the application.
 */

import type {
  User,
  Track,
  Playlist,
  Conversation,
  Message,
  Session,
  AuditLog,
  Notification,
  ApiResponse,
  PaginationData,
} from '@/types/api';
import type { ApiError } from '@/schemas/apiSchemas';

/**
 * Type guard for User
 */
export function isUser(value: unknown): value is User {
  return (
    typeof value === 'object' &&
    value !== null &&
    'id' in value &&
    'username' in value &&
    'email' in value &&
    'role' in value &&
    typeof (value as any).id === 'string' &&
    typeof (value as any).username === 'string' &&
    typeof (value as any).email === 'string' &&
    typeof (value as any).role === 'string'
  );
}

/**
 * Type guard for Track
 */
export function isTrack(value: unknown): value is Track {
  return (
    typeof value === 'object' &&
    value !== null &&
    'id' in value &&
    'title' in value &&
    'artist' in value &&
    'duration' in value &&
    typeof (value as any).id === 'string' &&
    typeof (value as any).title === 'string' &&
    typeof (value as any).artist === 'string' &&
    typeof (value as any).duration === 'number'
  );
}

/**
 * Type guard for Playlist
 */
export function isPlaylist(value: unknown): value is Playlist {
  return (
    typeof value === 'object' &&
    value !== null &&
    'id' in value &&
    'user_id' in value &&
    'title' in value &&
    'is_public' in value &&
    typeof (value as any).id === 'string' &&
    typeof (value as any).user_id === 'string' &&
    typeof (value as any).title === 'string' &&
    typeof (value as any).is_public === 'boolean'
  );
}

/**
 * Type guard for Conversation
 */
export function isConversation(value: unknown): value is Conversation {
  return (
    typeof value === 'object' &&
    value !== null &&
    'id' in value &&
    'name' in value &&
    'type' in value &&
    'creator_id' in value &&
    typeof (value as any).id === 'string' &&
    typeof (value as any).name === 'string' &&
    typeof (value as any).type === 'string' &&
    typeof (value as any).creator_id === 'string'
  );
}

/**
 * Type guard for Message
 */
export function isMessage(value: unknown): value is Message {
  return (
    typeof value === 'object' &&
    value !== null &&
    'id' in value &&
    'conversation_id' in value &&
    'sender_id' in value &&
    'content' in value &&
    typeof (value as any).id === 'string' &&
    typeof (value as any).conversation_id === 'string' &&
    typeof (value as any).sender_id === 'string' &&
    typeof (value as any).content === 'string'
  );
}

/**
 * Type guard for Session
 */
export function isSession(value: unknown): value is Session {
  return (
    typeof value === 'object' &&
    value !== null &&
    'id' in value &&
    'user_id' in value &&
    'ip_address' in value &&
    'user_agent' in value &&
    'expires_at' in value &&
    typeof (value as any).id === 'string' &&
    typeof (value as any).user_id === 'string' &&
    typeof (value as any).ip_address === 'string' &&
    typeof (value as any).user_agent === 'string' &&
    typeof (value as any).expires_at === 'string'
  );
}

/**
 * Type guard for AuditLog
 */
export function isAuditLog(value: unknown): value is AuditLog {
  return (
    typeof value === 'object' &&
    value !== null &&
    'id' in value &&
    'action' in value &&
    'resource' in value &&
    'timestamp' in value &&
    typeof (value as any).id === 'string' &&
    typeof (value as any).action === 'string' &&
    typeof (value as any).resource === 'string' &&
    typeof (value as any).timestamp === 'string'
  );
}

/**
 * Type guard for Notification
 */
export function isNotification(value: unknown): value is Notification {
  return (
    typeof value === 'object' &&
    value !== null &&
    'id' in value &&
    'user_id' in value &&
    'type' in value &&
    'content' in value &&
    'read' in value &&
    typeof (value as any).id === 'string' &&
    typeof (value as any).user_id === 'string' &&
    typeof (value as any).type === 'string' &&
    typeof (value as any).content === 'string' &&
    typeof (value as any).read === 'boolean'
  );
}

/**
 * Type guard for ApiError
 */
export function isApiError(value: unknown): value is ApiError {
  return (
    typeof value === 'object' &&
    value !== null &&
    'code' in value &&
    'message' in value &&
    'timestamp' in value &&
    typeof (value as any).code === 'number' &&
    typeof (value as any).message === 'string' &&
    typeof (value as any).timestamp === 'string'
  );
}

/**
 * Type guard for ApiResponse
 */
export function isApiResponse<T = unknown>(
  value: unknown,
): value is ApiResponse<T> {
  return (
    typeof value === 'object' &&
    value !== null &&
    'success' in value &&
    typeof (value as any).success === 'boolean'
  );
}

/**
 * Type guard for PaginationData
 */
export function isPaginationData(value: unknown): value is PaginationData {
  return (
    typeof value === 'object' &&
    value !== null &&
    'page' in value &&
    'limit' in value &&
    'total' in value &&
    'total_pages' in value &&
    'has_next' in value &&
    'has_prev' in value &&
    typeof (value as any).page === 'number' &&
    typeof (value as any).limit === 'number' &&
    typeof (value as any).total === 'number' &&
    typeof (value as any).total_pages === 'number' &&
    typeof (value as any).has_next === 'boolean' &&
    typeof (value as any).has_prev === 'boolean'
  );
}

/**
 * Type guard for array of Users
 */
export function isUserArray(value: unknown): value is User[] {
  return Array.isArray(value) && value.every((item) => isUser(item));
}

/**
 * Type guard for array of Tracks
 */
export function isTrackArray(value: unknown): value is Track[] {
  return Array.isArray(value) && value.every((item) => isTrack(item));
}

/**
 * Type guard for array of Playlists
 */
export function isPlaylistArray(value: unknown): value is Playlist[] {
  return Array.isArray(value) && value.every((item) => isPlaylist(item));
}

/**
 * Type guard for array of Conversations
 */
export function isConversationArray(value: unknown): value is Conversation[] {
  return Array.isArray(value) && value.every((item) => isConversation(item));
}

/**
 * Type guard for array of Messages
 */
export function isMessageArray(value: unknown): value is Message[] {
  return Array.isArray(value) && value.every((item) => isMessage(item));
}

/**
 * Type guard for array of Notifications
 */
export function isNotificationArray(value: unknown): value is Notification[] {
  return Array.isArray(value) && value.every((item) => isNotification(item));
}

/**
 * Type guard to check if value is a string UUID
 */
export function isUUID(value: unknown): value is string {
  if (typeof value !== 'string') {
    return false;
  }
  // UUID v4 pattern
  const uuidRegex =
    /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
  return uuidRegex.test(value);
}

/**
 * Type guard to check if value is a valid email
 */
export function isEmail(value: unknown): value is string {
  if (typeof value !== 'string') {
    return false;
  }
  const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
  return emailRegex.test(value);
}

/**
 * Type guard to check if value is a valid ISO8601 date string
 */
export function isISO8601Date(value: unknown): value is string {
  if (typeof value !== 'string') {
    return false;
  }
  // ISO8601 date pattern
  const isoDateRegex = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d{3})?Z?$/;
  if (!isoDateRegex.test(value)) {
    return false;
  }
  // Try to parse as date
  const date = new Date(value);
  return !isNaN(date.getTime());
}

/**
 * Type guard to check if value is a non-empty string
 */
export function isNonEmptyString(value: unknown): value is string {
  return typeof value === 'string' && value.length > 0;
}

/**
 * Type guard to check if value is a positive number
 */
export function isPositiveNumber(value: unknown): value is number {
  return typeof value === 'number' && value > 0 && !isNaN(value);
}

/**
 * Type guard to check if value is a non-negative number
 */
export function isNonNegativeNumber(value: unknown): value is number {
  return typeof value === 'number' && value >= 0 && !isNaN(value);
}

/**
 * Type guard to check if value is a valid URL
 */
export function isURL(value: unknown): value is string {
  if (typeof value !== 'string') {
    return false;
  }
  try {
    new URL(value);
    return true;
  } catch {
    return false;
  }
}

/**
 * Type guard to check if value is a plain object (not array, not null)
 */
export function isPlainObject(
  value: unknown,
): value is Record<string, unknown> {
  return (
    typeof value === 'object' &&
    value !== null &&
    !Array.isArray(value) &&
    Object.prototype.toString.call(value) === '[object Object]'
  );
}

/**
 * Type guard to check if value is an array of a specific type
 *
 * @param value - Value to check
 * @param itemGuard - Type guard function for array items
 * @returns True if value is an array and all items pass the guard
 */
export function isArrayOf<T>(
  value: unknown,
  itemGuard: (item: unknown) => item is T,
): value is T[] {
  return Array.isArray(value) && value.every(itemGuard);
}

/**
 * Type guard to check if value is not null or undefined
 */
export function isNotNull<T>(value: T | null | undefined): value is T {
  return value !== null && value !== undefined;
}

/**
 * Type guard to check if value is a defined (not undefined)
 */
export function isDefined<T>(value: T | undefined): value is T {
  return value !== undefined;
}

/**
 * Type guard to check if value is a number (including 0)
 */
export function isNumber(value: unknown): value is number {
  return typeof value === 'number' && !isNaN(value);
}

/**
 * Type guard to check if value is a boolean
 */
export function isBoolean(value: unknown): value is boolean {
  return typeof value === 'boolean';
}

/**
 * Type guard to check if value is a string
 */
export function isString(value: unknown): value is string {
  return typeof value === 'string';
}
