/**
 * Tests for State Cleanup
 * FE-STATE-012: Test cleanup functionality
 */

import { describe, it, expect, beforeEach, vi } from 'vitest';
import { create } from 'zustand';
import {
  cleanupState,
  cleanupMiddleware,
  performCleanup,
  type CleanupConfig,
} from './stateCleanup';

interface TestState {
  messages: Array<{ id: string; content: string; created_at: string }>;
  conversations: Array<{ id: string; name: string; updated_at: string }>;
  normalized: {
    byId: Record<string, { id: string; title: string; created_at: string }>;
    allIds: string[];
  };
  nested: Record<string, Array<{ id: string; content: string; timestamp: number }>>;
}

describe('stateCleanup', () => {
  beforeEach(() => {
    vi.useFakeTimers();
  });

  afterEach(() => {
    vi.useRealTimers();
  });

  describe('cleanupState', () => {
    it('should clean up array by size limit', () => {
      const state: TestState = {
        messages: [
          { id: '1', content: 'msg1', created_at: new Date().toISOString() },
          { id: '2', content: 'msg2', created_at: new Date().toISOString() },
          { id: '3', content: 'msg3', created_at: new Date().toISOString() },
        ],
        conversations: [],
        normalized: { byId: {}, allIds: [] },
        nested: {},
      };

      const configs: Record<string, CleanupConfig> = {
        messages: {
          strategy: 'size_limit',
          maxSize: 2,
        },
      };

      const cleaned = cleanupState(state, configs);

      expect(cleaned.messages.length).toBe(2);
    });

    it('should clean up array by age limit', () => {
      const now = Date.now();
      const oldDate = new Date(now - 2 * 60 * 60 * 1000).toISOString(); // 2 hours ago
      const recentDate = new Date(now - 30 * 60 * 1000).toISOString(); // 30 minutes ago

      const state: TestState = {
        messages: [
          { id: '1', content: 'old', created_at: oldDate },
          { id: '2', content: 'recent', created_at: recentDate },
        ],
        conversations: [],
        normalized: { byId: {}, allIds: [] },
        nested: {},
      };

      const configs: Record<string, CleanupConfig> = {
        messages: {
          strategy: 'age_limit',
          maxAge: 60 * 60 * 1000, // 1 hour
        },
      };

      const cleaned = cleanupState(state, configs);

      expect(cleaned.messages.length).toBe(1);
      expect(cleaned.messages[0].id).toBe('2');
    });

    it('should clean up normalized state by size', () => {
      const state: TestState = {
        messages: [],
        conversations: [],
        normalized: {
          byId: {
            '1': { id: '1', title: 'Item 1', created_at: new Date().toISOString() },
            '2': { id: '2', title: 'Item 2', created_at: new Date().toISOString() },
            '3': { id: '3', title: 'Item 3', created_at: new Date().toISOString() },
          },
          allIds: ['1', '2', '3'],
        },
        nested: {},
      };

      const configs: Record<string, CleanupConfig> = {
        normalized: {
          strategy: 'size_limit',
          maxSize: 2,
        },
      };

      const cleaned = cleanupState(state, configs);

      expect(cleaned.normalized.allIds.length).toBe(2);
      expect(Object.keys(cleaned.normalized.byId).length).toBe(2);
    });

    it('should clean up nested objects with arrays', () => {
      const now = Date.now();
      const state: TestState = {
        messages: [],
        conversations: [],
        normalized: { byId: {}, allIds: [] },
        nested: {
          'conv1': [
            { id: '1', content: 'msg1', timestamp: now - 2 * 60 * 60 * 1000 },
            { id: '2', content: 'msg2', timestamp: now - 30 * 60 * 1000 },
          ],
          'conv2': [
            { id: '3', content: 'msg3', timestamp: now - 10 * 60 * 1000 },
          ],
        },
      };

      const configs: Record<string, CleanupConfig> = {
        nested: {
          strategy: 'age_limit',
          maxAge: 60 * 60 * 1000, // 1 hour
        },
      };

      const cleaned = cleanupState(state, configs);

      expect(cleaned.nested['conv1'].length).toBe(1);
      expect(cleaned.nested['conv1'][0].id).toBe('2');
      expect(cleaned.nested['conv2'].length).toBe(1);
    });

    it('should apply both size and age limits', () => {
      const now = Date.now();
      const oldDate = new Date(now - 2 * 60 * 60 * 1000).toISOString();
      const recentDate = new Date(now - 30 * 60 * 1000).toISOString();

      const state: TestState = {
        messages: [
          { id: '1', content: 'old1', created_at: oldDate },
          { id: '2', content: 'old2', created_at: oldDate },
          { id: '3', content: 'recent1', created_at: recentDate },
          { id: '4', content: 'recent2', created_at: recentDate },
          { id: '5', content: 'recent3', created_at: recentDate },
        ],
        conversations: [],
        normalized: { byId: {}, allIds: [] },
        nested: {},
      };

      const configs: Record<string, CleanupConfig> = {
        messages: {
          strategy: 'both',
          maxSize: 3,
          maxAge: 60 * 60 * 1000, // 1 hour
        },
      };

      const cleaned = cleanupState(state, configs);

      // Should keep only recent messages (age limit filters out old1 and old2)
      // Then size limit keeps max 3 of the remaining recent messages
      expect(cleaned.messages.length).toBe(3);
      // All kept messages should be recent (not old)
      expect(cleaned.messages.every((m) => m.id === '3' || m.id === '4' || m.id === '5')).toBe(true);
    });

    it('should use custom cleanup function', () => {
      const state: TestState = {
        messages: [
          { id: '1', content: 'msg1', created_at: new Date().toISOString() },
          { id: '2', content: 'msg2', created_at: new Date().toISOString() },
        ],
        conversations: [],
        normalized: { byId: {}, allIds: [] },
        nested: {},
      };

      const configs: Record<string, CleanupConfig> = {
        messages: {
          strategy: 'custom',
          cleanupFn: (value) => {
            const messages = value as TestState['messages'];
            return messages.filter((m) => m.id !== '1');
          },
        },
      };

      const cleaned = cleanupState(state, configs);

      expect(cleaned.messages.length).toBe(1);
      expect(cleaned.messages[0].id).toBe('2');
    });
  });

  describe('cleanupMiddleware', () => {
    it('should add cleanup method to store', () => {
      const useTestStore = create<TestState & { cleanup?: () => void }>()(
        cleanupMiddleware(
          (set) => ({
            messages: [],
            conversations: [],
            normalized: { byId: {}, allIds: [] },
            nested: {},
          }),
          {
            storeName: 'TestStore',
            configs: {},
          },
        ),
      );

      const store = useTestStore.getState();
      expect(typeof store.cleanup).toBe('function');
    });

    it('should perform automatic cleanup when enabled', () => {
      const now = Date.now();
      const oldDate = new Date(now - 2 * 60 * 60 * 1000).toISOString();

      const useTestStore = create<TestState & { cleanup?: () => void }>()(
        cleanupMiddleware(
          (set) => ({
            messages: [
              { id: '1', content: 'old', created_at: oldDate },
            ],
            conversations: [],
            normalized: { byId: {}, allIds: [] },
            nested: {},
          }),
          {
            storeName: 'TestStore',
            configs: {
              messages: {
                strategy: 'age_limit',
                maxAge: 60 * 60 * 1000, // 1 hour
              },
            },
            autoCleanup: true,
            cleanupInterval: 1000, // 1 second for testing
          },
        ),
      );

      // Fast-forward time
      vi.advanceTimersByTime(2000);

      const state = useTestStore.getState();
      // Messages should be cleaned up
      expect(state.messages.length).toBe(0);
    });
  });

  describe('performCleanup', () => {
    it('should clean up state manually', () => {
      const state: TestState = {
        messages: [
          { id: '1', content: 'msg1', created_at: new Date().toISOString() },
          { id: '2', content: 'msg2', created_at: new Date().toISOString() },
          { id: '3', content: 'msg3', created_at: new Date().toISOString() },
        ],
        conversations: [],
        normalized: { byId: {}, allIds: [] },
        nested: {},
      };

      let currentState = state;
      const setState = (newState: Partial<TestState>) => {
        currentState = { ...currentState, ...newState };
      };
      const getState = () => currentState;

      const configs: Record<string, CleanupConfig> = {
        messages: {
          strategy: 'size_limit',
          maxSize: 2,
        },
      };

      performCleanup(getState, setState, configs, 'TestStore');

      expect(currentState.messages.length).toBe(2);
    });
  });
});

