/**
 * State Invalidation Utilities
 * FE-STATE-004: Invalidate stale state when data changes
 * 
 * Provides utilities for invalidating cached state, queries, and stores
 * when data changes on the server or locally
 */

import { responseCache } from '@/services/responseCache';
import { logger } from './logger';

/**
 * Types of state that can be invalidated
 */
export type InvalidationTarget = 
  | 'cache'           // Response cache
  | 'queries'         // TanStack Query cache
  | 'stores'          // Zustand stores
  | 'all';            // All of the above

/**
 * Resource types for granular invalidation
 */
export type ResourceType = 
  | 'tracks'
  | 'playlists'
  | 'users'
  | 'conversations'
  | 'roles'
  | 'library'
  | 'auth'
  | 'ui'
  | 'all';

/**
 * Options for state invalidation
 */
export interface InvalidationOptions {
  /** Target to invalidate */
  target?: InvalidationTarget;
  /** Resource type to invalidate */
  resourceType?: ResourceType;
  /** Specific resource ID */
  resourceId?: string;
  /** Whether to invalidate all state */
  invalidateAll?: boolean;
  /** Query keys to invalidate (for TanStack Query) */
  queryKeys?: (string | number)[][];
  /** Store names to invalidate (for Zustand stores) */
  storeNames?: string[];
}

/**
 * FE-STATE-004: Invalidate stale state
 * 
 * Invalidates cached state, queries, and stores based on the provided options.
 * This should be called after mutations or when data changes are detected.
 * 
 * @param options Invalidation options
 * 
 * @example
 * ```typescript
 * // Invalidate all cache for tracks
 * invalidateState({ resourceType: 'tracks', target: 'cache' });
 * 
 * // Invalidate specific playlist
 * invalidateState({ resourceType: 'playlists', resourceId: '123', target: 'all' });
 * 
 * // Invalidate after mutation
 * await updatePlaylist(id, data);
 * invalidateState({ resourceType: 'playlists', resourceId: id });
 * ```
 */
export function invalidateState(options: InvalidationOptions = {}): void {
  const {
    target = 'all',
    resourceType,
    resourceId,
    invalidateAll = false,
    queryKeys = [],
    storeNames = [],
  } = options;

  try {
    // Invalidate response cache
    if (target === 'cache' || target === 'all') {
      if (invalidateAll) {
        responseCache.clear();
        logger.debug('[StateInvalidation] Cleared all response cache');
      } else if (resourceType) {
        invalidateCacheByResource(resourceType, resourceId);
      }
    }

    // Invalidate TanStack Query cache
    if (target === 'queries' || target === 'all') {
      invalidateQueries(queryKeys, resourceType, resourceId);
    }

    // Invalidate Zustand stores
    if (target === 'stores' || target === 'all') {
      invalidateStores(storeNames, resourceType, resourceId);
    }

    logger.debug('[StateInvalidation] State invalidated', {
      target,
      resourceType,
      resourceId,
      invalidateAll,
    });
  } catch (error) {
    logger.error('[StateInvalidation] Error invalidating state:', error);
  }
}

/**
 * Invalidate cache by resource type
 */
function invalidateCacheByResource(
  resourceType: ResourceType,
  resourceId?: string,
): void {
  const patterns: Record<ResourceType, string[]> = {
    tracks: ['/tracks', '/library/tracks'],
    playlists: ['/playlists'],
    users: ['/users', '/auth/me'],
    conversations: ['/conversations'],
    roles: ['/roles'],
    library: ['/library', '/tracks'],
    auth: ['/auth'],
    ui: [],
    all: [],
  };

  if (resourceType === 'all') {
    responseCache.clear();
    return;
  }

  const patternsToInvalidate = patterns[resourceType] || [];
  
  for (const pattern of patternsToInvalidate) {
    responseCache.invalidate(pattern);
  }

  // If resourceId is provided, invalidate specific resource
  if (resourceId) {
    for (const pattern of patternsToInvalidate) {
      responseCache.invalidate(`${pattern}/${resourceId}`);
    }
  }
}

/**
 * Invalidate TanStack Query queries
 * 
 * Note: This function emits events that should be handled by components
 * using TanStack Query. Since we can't access QueryClient directly outside
 * of React components, we use a custom event system.
 */
function invalidateQueries(
  queryKeys: (string | number)[][],
  resourceType?: ResourceType,
  resourceId?: string,
): void {
  // Emit custom event for query invalidation
  // Components using TanStack Query should listen to this event
  if (typeof window !== 'undefined') {
    const event = new CustomEvent('veza:invalidate-queries', {
      detail: {
        queryKeys,
        resourceType,
        resourceId,
      },
    });
    window.dispatchEvent(event);
    logger.debug('[StateInvalidation] Dispatched query invalidation event', {
      queryKeys,
      resourceType,
      resourceId,
    });
  }
}

/**
 * Invalidate Zustand stores
 */
function invalidateStores(
  storeNames: string[],
  resourceType?: ResourceType,
  resourceId?: string,
): void {
  // Map resource types to store names
  const resourceToStores: Record<ResourceType, string[]> = {
    tracks: ['library'],
    playlists: ['library'],
    users: ['auth'],
    conversations: ['chat'],
    roles: [],
    library: ['library'],
    auth: ['auth'],
    ui: ['ui'],
    all: ['auth', 'library', 'chat', 'ui'],
  };

  const storesToInvalidate = storeNames.length > 0
    ? storeNames
    : resourceType
      ? resourceToStores[resourceType] || []
      : [];

  for (const storeName of storesToInvalidate) {
    try {
      invalidateStore(storeName, resourceType, resourceId);
    } catch (error) {
      logger.warn(`[StateInvalidation] Failed to invalidate store ${storeName}:`, error);
    }
  }
}

/**
 * Invalidate a specific store
 */
function invalidateStore(
  storeName: string,
  resourceType?: ResourceType,
  resourceId?: string,
): void {
  try {
    switch (storeName) {
      case 'auth':
        const { useAuthStore } = require('@/stores/auth');
        // Refresh user data
        useAuthStore.getState().refreshUser?.();
        break;

      case 'library':
        const { useLibraryStore } = require('@/stores/library');
        const libraryStore = useLibraryStore.getState();
        // Clear items and refetch if needed
        if (resourceType === 'tracks' || resourceType === 'library') {
          libraryStore.clearItems?.();
          // Optionally refetch
          // libraryStore.fetchItems?.();
        }
        break;

      case 'chat':
        const { useChatStore } = require('@/stores/chat');
        const chatStore = useChatStore.getState();
        // Refetch conversations if needed
        if (resourceType === 'conversations') {
          chatStore.fetchConversations?.();
        }
        break;

      case 'ui':
        // UI store doesn't need invalidation as it's user preferences
        break;

      default:
        logger.warn(`[StateInvalidation] Unknown store: ${storeName}`);
    }
  } catch (error) {
    logger.error(`[StateInvalidation] Error invalidating store ${storeName}:`, error);
  }
}

/**
 * FE-STATE-004: Helper to invalidate state after a mutation
 * 
 * This is a convenience function that automatically determines what to invalidate
 * based on the mutation endpoint.
 * 
 * @param url The mutation URL
 * @param method The HTTP method
 * 
 * @example
 * ```typescript
 * // In apiClient interceptor
 * if (isMutation) {
 *   invalidateStateAfterMutation(response.config.url, method);
 * }
 * ```
 */
export function invalidateStateAfterMutation(
  url: string | undefined,
  method: string,
): void {
  if (!url) {
    return;
  }

  // Determine resource type from URL
  let resourceType: ResourceType | undefined;
  let resourceId: string | undefined;

  if (url.includes('/tracks/')) {
    resourceType = 'tracks';
    const match = url.match(/\/tracks\/([^/]+)/);
    resourceId = match ? match[1] : undefined;
  } else if (url.includes('/playlists/')) {
    resourceType = 'playlists';
    const match = url.match(/\/playlists\/([^/]+)/);
    resourceId = match ? match[1] : undefined;
  } else if (url.includes('/users/') || url.includes('/auth/')) {
    resourceType = 'users';
    const match = url.match(/\/(users|auth)\/([^/]+)/);
    resourceId = match ? match[2] : undefined;
  } else if (url.includes('/conversations/')) {
    resourceType = 'conversations';
    const match = url.match(/\/conversations\/([^/]+)/);
    resourceId = match ? match[1] : undefined;
  } else if (url.includes('/roles/')) {
    resourceType = 'roles';
    const match = url.match(/\/roles\/([^/]+)/);
    resourceId = match ? match[1] : undefined;
  }

  if (resourceType) {
    invalidateState({
      resourceType,
      resourceId,
      target: 'all',
    });
  } else {
    // If we can't determine the resource type, invalidate all cache
    invalidateState({
      target: 'cache',
      invalidateAll: true,
    });
  }
}

