/**
 * BroadcastChannel State Synchronization
 * FE-STATE-002: Synchronize Zustand state across browser tabs using BroadcastChannel
 * 
 * This middleware allows Zustand stores to automatically sync their state
 * across all open tabs/windows of the same origin.
 */

import { StateCreator, StoreMutatorIdentifier } from 'zustand';

/**
 * Options for broadcast synchronization
 */
export interface BroadcastSyncOptions {
  /** Channel name for BroadcastChannel (default: store name) */
  channelName?: string;
  /** Whether to enable synchronization (default: true) */
  enabled?: boolean;
  /** Function to filter which state changes should be synced */
  shouldSync?: (state: any, prevState: any) => boolean;
}

/**
 * BroadcastChannel message format
 */
interface BroadcastMessage {
  type: 'state-update' | 'state-request' | 'state-response';
  storeName: string;
  state?: any;
  timestamp: number;
}

/**
 * Create a BroadcastChannel instance for a store
 */
function createBroadcastChannel(storeName: string): BroadcastChannel | null {
  if (typeof window === 'undefined' || !window.BroadcastChannel) {
    console.warn('[BroadcastSync] BroadcastChannel not supported in this environment');
    return null;
  }

  try {
    return new BroadcastChannel(`veza-store-${storeName}`);
  } catch (error) {
    console.warn(`[BroadcastSync] Failed to create BroadcastChannel for ${storeName}:`, error);
    return null;
  }
}

/**
 * Zustand middleware for BroadcastChannel synchronization
 * 
 * @example
 * ```typescript
 * export const useMyStore = create<MyState>()(
 *   broadcastSync(
 *     (set, get) => ({
 *       // store implementation
 *     }),
 *     { channelName: 'my-store' }
 *   )
 * );
 * ```
 */
export function broadcastSync<T extends object>(
  config: StateCreator<T>,
  options: BroadcastSyncOptions = {},
): StateCreator<T> {
  return (set, get, api) => {
    const storeName = options.channelName || 'default-store';
    const enabled = options.enabled !== false;
    const shouldSync = options.shouldSync || (() => true);

    let channel: BroadcastChannel | null = null;
    let isReceivingUpdate = false;
    let lastState: T | null = null;

    // Initialize BroadcastChannel
    if (enabled) {
      channel = createBroadcastChannel(storeName);

      if (channel) {
        // Listen for state updates from other tabs
        channel.onmessage = (event: MessageEvent<BroadcastMessage>) => {
          const message = event.data;

          if (message.type === 'state-update' && message.state) {
            // Ignore updates from the same tab
            if (isReceivingUpdate) {
              return;
            }

            // Check if we should sync this update
            if (shouldSync(message.state, lastState)) {
              isReceivingUpdate = true;
              set(message.state as T);
              lastState = message.state;
              
              // Reset flag after a short delay
              setTimeout(() => {
                isReceivingUpdate = false;
              }, 100);
            }
          } else if (message.type === 'state-request') {
            // Another tab is requesting the current state
            const currentState = get();
            channel.postMessage({
              type: 'state-response',
              storeName,
              state: currentState,
              timestamp: Date.now(),
            } as BroadcastMessage);
          } else if (message.type === 'state-response' && message.state) {
            // Received state from another tab (initial sync)
            if (!lastState) {
              isReceivingUpdate = true;
              set(message.state as T);
              lastState = message.state;
              
              setTimeout(() => {
                isReceivingUpdate = false;
              }, 100);
            }
          }
        };

        // Request initial state from other tabs on startup
        channel.postMessage({
          type: 'state-request',
          storeName,
          timestamp: Date.now(),
        } as BroadcastMessage);
      }
    }

    // Create the store with wrapped set function
    const store = config(
      (...args) => {
        if (!isReceivingUpdate) {
          set(...args);
          
          // Broadcast state update to other tabs
          if (channel && enabled) {
            const newState = get();
            
            if (shouldSync(newState, lastState)) {
              channel.postMessage({
                type: 'state-update',
                storeName,
                state: newState,
                timestamp: Date.now(),
              } as BroadcastMessage);
              
              lastState = newState;
            }
          }
        } else {
          // If we're receiving an update, just set without broadcasting
          set(...args);
        }
      },
      get,
      api,
    );

    return store;
  };
}

/**
 * Helper to create a synchronized store with both persistence and broadcast sync
 * 
 * @example
 * ```typescript
 * export const useMyStore = create<MyState>()(
 *   persist(
 *     broadcastSync(
 *       (set, get) => ({
 *         // store implementation
 *       }),
 *       { channelName: 'my-store' }
 *     ),
 *     { name: 'my-store-storage' }
 *   )
 * );
 * ```
 */
export function createSynchronizedStore<T extends object>(
  config: StateCreator<T>,
  options: {
    persist?: { name: string; partialize?: (state: T) => Partial<T> };
    broadcast?: BroadcastSyncOptions;
  } = {},
) {
  let store = config;

  // Apply broadcast sync if enabled
  if (options.broadcast?.enabled !== false) {
    store = broadcastSync(store, options.broadcast) as StateCreator<T>;
  }

  // Apply persistence if configured
  if (options.persist) {
    const { persist: persistMiddleware } = require('zustand/middleware');
    store = persistMiddleware(store, {
      name: options.persist.name,
      partialize: options.persist.partialize,
    }) as StateCreator<T>;
  }

  return store;
}

