import { describe, it, expect, beforeEach, vi, afterEach } from 'vitest';
import { act } from '@testing-library/react';
import { toast } from '../lib/toast';
import { useWorkflowStore } from './workflowStore';
import { workflowService } from '../services/workflows';
import { Workflow } from '../types/workflow';

// Mock the workflow service
vi.mock('../services/workflows', () => ({
  workflowService: {
    activateWorkflow: vi.fn(),
    deactivateWorkflow: vi.fn(),
  },
}));

// Mock custom toast
vi.mock('../lib/toast', () => ({
  toast: {
    success: vi.fn(),
    error: vi.fn(),
    info: vi.fn(),
  },
}));

describe('workflowStore', () => {
  beforeEach(() => {
    // Reset store to initial state
    useWorkflowStore.getState().reset();
    vi.clearAllMocks();
    vi.useFakeTimers();
  });

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

  describe('initial state', () => {
    it('should have correct initial values', () => {
      const state = useWorkflowStore.getState();
      expect(state.id).toBeNull();
      expect(state.name).toBe('Untitled Workflow');
      expect(state.isActive).toBe(false);
      expect(state.initialName).toBeNull();
      expect(state.isLoading).toBe(false);
      expect(state.isSaving).toBe(false);
      expect(state.isActivating).toBe(false);
      expect(state.loadError).toBeNull();
    });
  });

  describe('setters', () => {
    it('should update id', () => {
      act(() => useWorkflowStore.getState().setId(123));
      expect(useWorkflowStore.getState().id).toBe(123);
    });

    it('should update id to null', () => {
      act(() => useWorkflowStore.getState().setId(123));
      act(() => useWorkflowStore.getState().setId(null));
      expect(useWorkflowStore.getState().id).toBeNull();
    });

    it('should update name', () => {
      act(() => useWorkflowStore.getState().setName('My Workflow'));
      expect(useWorkflowStore.getState().name).toBe('My Workflow');
    });

    it('should update initialName', () => {
      act(() => useWorkflowStore.getState().setInitialName('Original Name'));
      expect(useWorkflowStore.getState().initialName).toBe('Original Name');
    });

    it('should update active state', () => {
      act(() => useWorkflowStore.getState().setActive(true));
      expect(useWorkflowStore.getState().isActive).toBe(true);
    });

    it('should update isSaving state', () => {
      act(() => useWorkflowStore.getState().setIsSaving(true));
      expect(useWorkflowStore.getState().isSaving).toBe(true);
    });
  });

  describe('hasNameChanged', () => {
    it('should return false when initialName is null', () => {
      expect(useWorkflowStore.getState().hasNameChanged()).toBe(false);
    });

    it('should return false when name matches initialName', () => {
      act(() => {
        useWorkflowStore.getState().setName('Test');
        useWorkflowStore.getState().setInitialName('Test');
      });
      expect(useWorkflowStore.getState().hasNameChanged()).toBe(false);
    });

    it('should return true when name differs from initialName', () => {
      act(() => {
        useWorkflowStore.getState().setName('Updated Name');
        useWorkflowStore.getState().setInitialName('Original Name');
      });
      expect(useWorkflowStore.getState().hasNameChanged()).toBe(true);
    });
  });

  describe('activate', () => {
    it('should not activate when id is null', async () => {
      const result = await useWorkflowStore.getState().activate();
      expect(result).toBe(false);
      expect(workflowService.activateWorkflow).not.toHaveBeenCalled();
    });

    it('should not activate when already saving', async () => {
      act(() => {
        useWorkflowStore.getState().setId(1);
        useWorkflowStore.getState().setIsSaving(true);
      });
      const result = await useWorkflowStore.getState().activate();
      expect(result).toBe(false);
      expect(workflowService.activateWorkflow).not.toHaveBeenCalled();
    });

    it('should not activate when already activating', async () => {
      act(() => {
        useWorkflowStore.getState().setId(1);
      });

      // Start an activation
      vi.mocked(workflowService.activateWorkflow).mockImplementation(
        () => new Promise(() => {}), // Never resolves
      );

      void useWorkflowStore.getState().activate();

      // Wait for the state to update
      await vi.advanceTimersByTimeAsync(0);

      // Try to activate again while first is in progress
      const result = await useWorkflowStore.getState().activate();
      expect(result).toBe(false);
    });

    it('should successfully activate workflow', async () => {
      vi.mocked(workflowService.activateWorkflow).mockResolvedValue({} as Workflow);

      act(() => useWorkflowStore.getState().setId(1));

      let result: boolean;
      await act(async () => {
        result = await useWorkflowStore.getState().activate();
      });

      expect(result!).toBe(true);
      expect(useWorkflowStore.getState().isActive).toBe(true);
      expect(toast.success).toHaveBeenCalled();
      expect(useWorkflowStore.getState().isActivating).toBe(false);
      expect(workflowService.activateWorkflow).toHaveBeenCalledWith(1);
    });

    it('should set isActivating to true during activation', async () => {
      let resolveActivate: () => void;
      vi.mocked(workflowService.activateWorkflow).mockImplementation(
        () =>
          new Promise((resolve) => {
            resolveActivate = () => resolve({} as Workflow);
          }),
      );

      act(() => useWorkflowStore.getState().setId(1));

      const activatePromise = useWorkflowStore.getState().activate();

      // Check state during activation
      await vi.advanceTimersByTimeAsync(0);
      expect(useWorkflowStore.getState().isActivating).toBe(true);

      // Resolve and complete
      await act(async () => {
        resolveActivate();
        await activatePromise;
      });

      expect(useWorkflowStore.getState().isActivating).toBe(false);
    });

    it('should handle activation errors', async () => {
      vi.mocked(workflowService.activateWorkflow).mockRejectedValue(new Error('Activation failed'));
      act(() => useWorkflowStore.getState().setId(1));

      let result: boolean;
      await act(async () => {
        result = await useWorkflowStore.getState().activate();
      });

      expect(result!).toBe(false);
      expect(toast.error).toHaveBeenCalledWith('Activation failed');
      expect(useWorkflowStore.getState().isActivating).toBe(false);
      expect(useWorkflowStore.getState().isActive).toBe(false);
    });
  });

  describe('deactivate', () => {
    it('should not deactivate when id is null', async () => {
      const result = await useWorkflowStore.getState().deactivate();
      expect(result).toBe(false);
      expect(workflowService.deactivateWorkflow).not.toHaveBeenCalled();
    });

    it('should not deactivate when saving', async () => {
      act(() => {
        useWorkflowStore.getState().setId(1);
        useWorkflowStore.getState().setIsSaving(true);
      });
      const result = await useWorkflowStore.getState().deactivate();
      expect(result).toBe(false);
    });

    it('should successfully deactivate workflow', async () => {
      vi.mocked(workflowService.deactivateWorkflow).mockResolvedValue({} as Workflow);

      act(() => {
        useWorkflowStore.getState().setId(1);
        useWorkflowStore.getState().setActive(true);
      });

      let result: boolean;
      await act(async () => {
        result = await useWorkflowStore.getState().deactivate();
      });

      expect(result!).toBe(true);
      expect(useWorkflowStore.getState().isActive).toBe(false);
      expect(toast.success).toHaveBeenCalled();
      expect(useWorkflowStore.getState().isActivating).toBe(false);
      expect(workflowService.deactivateWorkflow).toHaveBeenCalledWith(1);
    });

    it('should handle deactivation errors', async () => {
      vi.mocked(workflowService.deactivateWorkflow).mockRejectedValue(
        new Error('Deactivation failed'),
      );
      act(() => {
        useWorkflowStore.getState().setId(1);
        useWorkflowStore.getState().setActive(true);
      });

      let result: boolean;
      await act(async () => {
        result = await useWorkflowStore.getState().deactivate();
      });

      expect(result!).toBe(false);
      expect(toast.error).toHaveBeenCalledWith('Deactivation failed');
      expect(useWorkflowStore.getState().isActivating).toBe(false);
    });
  });

  describe('reset', () => {
    it('should reset all state to initial values', () => {
      act(() => {
        useWorkflowStore.getState().setId(123);
        useWorkflowStore.getState().setName('Modified');
        useWorkflowStore.getState().setActive(true);
        useWorkflowStore.getState().setInitialName('Original');
        useWorkflowStore.getState().setIsSaving(true);
      });

      act(() => useWorkflowStore.getState().reset());

      const state = useWorkflowStore.getState();
      expect(state.id).toBeNull();
      expect(state.name).toBe('Untitled Workflow');
      expect(state.isActive).toBe(false);
      expect(state.initialName).toBeNull();
      expect(state.isSaving).toBe(false);
    });
  });
});
