import { describe, it, expect, vi, beforeEach } from 'vitest';
import { useProjectStore } from '../projectStore';
import { Project, CreateProjectRequest } from '../../types/project';

// Mock Tauri API
vi.mock('@tauri-apps/api/core', () => ({
  invoke: vi.fn(),
}));

const { invoke } = await import('@tauri-apps/api/core');
const mockInvoke = vi.mocked(invoke);

const mockProject: Project = {
  id: '1',
  name: 'Test Project',
  path: '/path/to/project',
  description: 'Test description',
  created_at: '2023-01-01T00:00:00Z',
  updated_at: '2023-01-02T00:00:00Z',
  is_active: true,
};

describe('ProjectStore', () => {
  beforeEach(() => {
    vi.clearAllMocks();
    // 重置 store 状态
    useProjectStore.setState({
      projects: [],
      currentProject: null,
      isLoading: false,
      error: null,
    });
  });

  describe('loadProjects', () => {
    it('loads projects successfully', async () => {
      const mockProjects = [mockProject];
      mockInvoke.mockResolvedValueOnce(mockProjects);

      const store = useProjectStore.getState();
      await store.loadProjects();

      const state = useProjectStore.getState();
      expect(state.projects).toEqual(mockProjects);
      expect(state.isLoading).toBe(false);
      expect(state.error).toBeNull();
      expect(mockInvoke).toHaveBeenCalledWith('get_all_projects');
    });

    it('handles load projects error', async () => {
      const errorMessage = 'Failed to load projects';
      mockInvoke.mockRejectedValueOnce(errorMessage);

      const store = useProjectStore.getState();
      await store.loadProjects();

      const state = useProjectStore.getState();
      expect(state.projects).toEqual([]);
      expect(state.isLoading).toBe(false);
      expect(state.error).toBe(errorMessage);
    });
  });

  describe('createProject', () => {
    it('creates project successfully', async () => {
      const createRequest: CreateProjectRequest = {
        name: 'New Project',
        path: '/new/path',
        description: 'New description',
      };

      mockInvoke.mockResolvedValueOnce(mockProject);

      const store = useProjectStore.getState();
      await store.createProject(createRequest);

      const state = useProjectStore.getState();
      expect(state.projects).toContain(mockProject);
      expect(state.isLoading).toBe(false);
      expect(state.error).toBeNull();
      expect(mockInvoke).toHaveBeenCalledWith('create_project', { request: createRequest });
    });

    it('handles create project error', async () => {
      const createRequest: CreateProjectRequest = {
        name: 'New Project',
        path: '/new/path',
      };

      const errorMessage = 'Failed to create project';
      mockInvoke.mockRejectedValueOnce(errorMessage);

      const store = useProjectStore.getState();
      
      await expect(store.createProject(createRequest)).rejects.toBe(errorMessage);

      const state = useProjectStore.getState();
      expect(state.isLoading).toBe(false);
      expect(state.error).toBe(errorMessage);
    });
  });

  describe('updateProject', () => {
    it('updates project successfully', async () => {
      const updatedProject = { ...mockProject, name: 'Updated Project' };
      
      // 设置初始状态
      useProjectStore.setState({
        projects: [mockProject],
        currentProject: mockProject,
        isLoading: false,
        error: null,
      });

      mockInvoke.mockResolvedValueOnce(updatedProject);

      const store = useProjectStore.getState();
      await store.updateProject('1', { name: 'Updated Project' });

      const state = useProjectStore.getState();
      expect(state.projects[0]).toEqual(updatedProject);
      expect(state.currentProject).toEqual(updatedProject);
      expect(state.isLoading).toBe(false);
      expect(state.error).toBeNull();
    });
  });

  describe('deleteProject', () => {
    it('deletes project successfully', async () => {
      // 设置初始状态
      useProjectStore.setState({
        projects: [mockProject],
        currentProject: mockProject,
        isLoading: false,
        error: null,
      });

      mockInvoke.mockResolvedValueOnce(undefined);

      const store = useProjectStore.getState();
      await store.deleteProject('1');

      const state = useProjectStore.getState();
      expect(state.projects).toEqual([]);
      expect(state.currentProject).toBeNull();
      expect(state.isLoading).toBe(false);
      expect(state.error).toBeNull();
    });
  });

  describe('setCurrentProject', () => {
    it('sets current project', () => {
      const store = useProjectStore.getState();
      store.setCurrentProject(mockProject);

      const state = useProjectStore.getState();
      expect(state.currentProject).toEqual(mockProject);
    });

    it('clears current project', () => {
      useProjectStore.setState({ currentProject: mockProject });
      
      const store = useProjectStore.getState();
      store.setCurrentProject(null);

      const state = useProjectStore.getState();
      expect(state.currentProject).toBeNull();
    });
  });

  describe('clearError', () => {
    it('clears error state', () => {
      useProjectStore.setState({ error: 'Some error' });
      
      const store = useProjectStore.getState();
      store.clearError();

      const state = useProjectStore.getState();
      expect(state.error).toBeNull();
    });
  });

  describe('validateProjectPath', () => {
    it('validates project path successfully', async () => {
      mockInvoke.mockResolvedValueOnce(true);

      const store = useProjectStore.getState();
      const result = await store.validateProjectPath('/valid/path');

      expect(result).toBe(true);
      expect(mockInvoke).toHaveBeenCalledWith('validate_project_path', { path: '/valid/path' });
    });

    it('handles validation error', async () => {
      mockInvoke.mockRejectedValueOnce(new Error('Validation failed'));

      const store = useProjectStore.getState();
      const result = await store.validateProjectPath('/invalid/path');

      expect(result).toBe(false);
    });
  });

  describe('getDefaultProjectName', () => {
    it('gets default project name successfully', async () => {
      mockInvoke.mockResolvedValueOnce('ProjectName');

      const store = useProjectStore.getState();
      const result = await store.getDefaultProjectName('/path/to/ProjectName');

      expect(result).toBe('ProjectName');
      expect(mockInvoke).toHaveBeenCalledWith('get_default_project_name', { path: '/path/to/ProjectName' });
    });

    it('handles get default name error', async () => {
      mockInvoke.mockRejectedValueOnce(new Error('Failed to get name'));

      const store = useProjectStore.getState();
      const result = await store.getDefaultProjectName('/invalid/path');

      expect(result).toBe('');
    });
  });
});
