/**
 * Tests for State Middleware
 * FE-STATE-010: Test logging, analytics, and error handling
 */

import { describe, it, expect, vi, beforeEach } from 'vitest';
import { create } from 'zustand';
import { stateMiddleware, setAnalyticsHandler, setErrorHandler, type AnalyticsEvent } from './stateMiddleware';

interface TestState {
  count: number;
  name: string;
  increment: () => void;
  setName: (name: string) => void;
  throwError: () => void;
  asyncAction: () => Promise<string>;
}

describe('stateMiddleware', () => {
  let analyticsEvents: AnalyticsEvent[] = [];
  let errorEvents: Array<{ error: Error; context: unknown }> = [];

  beforeEach(() => {
    analyticsEvents = [];
    errorEvents = [];
    
    setAnalyticsHandler((event) => {
      analyticsEvents.push(event);
    });
    
    setErrorHandler((error, context) => {
      errorEvents.push({ error, context });
    });
  });

  it('should log state changes in development', () => {
    const consoleSpy = vi.spyOn(console, 'debug').mockImplementation(() => {});
    
    const useTestStore = create<TestState>()(
      stateMiddleware(
        (set) => ({
          count: 0,
          name: '',
          increment: () => set((state) => ({ count: state.count + 1 })),
          setName: (name: string) => set({ name }),
          throwError: () => {
            throw new Error('Test error');
          },
          asyncAction: async () => {
            return 'success';
          },
        }),
        { storeName: 'TestStore', enableLogging: true },
      ),
    );

    const store = useTestStore.getState();
    store.increment();

    expect(consoleSpy).toHaveBeenCalled();
    consoleSpy.mockRestore();
  });

  it('should track analytics events', () => {
    const useTestStore = create<TestState>()(
      stateMiddleware(
        (set) => ({
          count: 0,
          name: '',
          increment: () => set((state) => ({ count: state.count + 1 })),
          setName: (name: string) => set({ name }),
          throwError: () => {
            throw new Error('Test error');
          },
          asyncAction: async () => {
            return 'success';
          },
        }),
        { storeName: 'TestStore', enableAnalytics: true },
      ),
    );

    const store = useTestStore.getState();
    store.increment();

    expect(analyticsEvents.length).toBeGreaterThan(0);
    expect(analyticsEvents.some((e) => e.type === 'state_change')).toBe(true);
    expect(analyticsEvents.some((e) => e.type === 'action_called')).toBe(true);
  });

  it('should handle errors', () => {
    const useTestStore = create<TestState>()(
      stateMiddleware(
        (set) => ({
          count: 0,
          name: '',
          increment: () => set((state) => ({ count: state.count + 1 })),
          setName: (name: string) => set({ name }),
          throwError: () => {
            throw new Error('Test error');
          },
          asyncAction: async () => {
            return 'success';
          },
        }),
        { storeName: 'TestStore', enableErrorHandling: true },
      ),
    );

    const store = useTestStore.getState();
    
    expect(() => store.throwError()).toThrow('Test error');
    expect(errorEvents.length).toBeGreaterThan(0);
    expect(errorEvents[0].error.message).toBe('Test error');
  });

  it('should track async action performance', async () => {
    // Clear previous events
    analyticsEvents = [];
    
    const useTestStore = create<TestState>()(
      stateMiddleware(
        (set) => ({
          count: 0,
          name: '',
          increment: () => set((state) => ({ count: state.count + 1 })),
          setName: (name: string) => set({ name }),
          throwError: () => {
            throw new Error('Test error');
          },
          asyncAction: async () => {
            await new Promise((resolve) => setTimeout(resolve, 50));
            return 'success';
          },
        }),
        { storeName: 'TestStore', enableAnalytics: true },
      ),
    );

    const store = useTestStore.getState();
    await store.asyncAction();

    // Wait a bit for async analytics to be tracked
    await new Promise((resolve) => setTimeout(resolve, 10));

    const performanceEvents = analyticsEvents.filter(
      (e) => e.type === 'performance_metric' && e.action === 'asyncAction',
    );
    expect(performanceEvents.length).toBeGreaterThan(0);
  });

  it('should sanitize sensitive data', () => {
    const consoleSpy = vi.spyOn(console, 'debug').mockImplementation(() => {});
    
    const useTestStore = create<{ password: string; token: string; name: string; updateName: (name: string) => void }>()(
      stateMiddleware(
        (set) => ({
          password: 'secret123',
          token: 'abc123',
          name: 'John',
          updateName: (name: string) => set({ name }),
        }),
        {
          storeName: 'TestStore',
          enableLogging: true,
          sanitizeState: (state) => {
            if (typeof state === 'object' && state !== null) {
              const sanitized = { ...state as Record<string, unknown> };
              sanitized.password = '[REDACTED]';
              sanitized.token = '[REDACTED]';
              return sanitized;
            }
            return state;
          },
        },
      ),
    );

    const store = useTestStore.getState();
    store.updateName('Jane'); // Trigger state change
    
    // Verify that logging was called (the middleware logs state changes)
    // The logger uses [DEBUG] prefix, so we check for that
    expect(consoleSpy).toHaveBeenCalled();
    consoleSpy.mockRestore();
  });

  it('should respect shouldLog filter', () => {
    const consoleSpy = vi.spyOn(console, 'debug').mockImplementation(() => {});
    
    const useTestStore = create<{ count: number; increment: () => void }>()(
      stateMiddleware(
        (set) => ({
          count: 0,
          increment: () => set((state) => ({ count: state.count + 1 })),
        }),
        {
          storeName: 'TestStore',
          enableLogging: true,
          shouldLog: () => false, // Don't log state changes
        },
      ),
    );

    const store = useTestStore.getState();
    store.increment();

    // Action calls are still logged, but state changes are filtered
    // So we expect at least one call (action call), but state change logging is filtered
    const calls = consoleSpy.mock.calls;
    const stateChangeCalls = calls.filter((call) => 
      call[0]?.toString().includes('State change')
    );
    expect(stateChangeCalls.length).toBe(0);
    consoleSpy.mockRestore();
  });

  it('should respect shouldTrack filter', () => {
    const useTestStore = create<{ count: number; increment: () => void }>()(
      stateMiddleware(
        (set) => ({
          count: 0,
          increment: () => set((state) => ({ count: state.count + 1 })),
        }),
        {
          storeName: 'TestStore',
          enableAnalytics: true,
          shouldTrack: (action) => action !== 'increment', // Don't track increment
        },
      ),
    );

    const store = useTestStore.getState();
    store.increment();

    // Should not track increment action
    const actionEvents = analyticsEvents.filter(
      (e) => e.type === 'action_called' && e.action === 'increment',
    );
    expect(actionEvents.length).toBe(0);
  });
});

