// ========= Copyright 2025-2026 @ Eigent.ai All Rights Reserved. =========
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ========= Copyright 2025-2026 @ Eigent.ai All Rights Reserved. =========

/**
 * Integration Test: Case 1 - New Project
 * 
 * Tests the complete flow of creating a new project and sending the first message.
 * 
 * Flow:
 * 1. User creates a new project with initial message
 * 2. System automatically creates initial chatStore
 * 3. Task starts executing
 * 
 * This is the most common user journey and serves as the foundation for all other cases.
 */

import { describe, it, expect, beforeEach, vi, afterEach } from 'vitest'
import { act, renderHook, waitFor } from '@testing-library/react'
import { generateUniqueId } from '../../../src/lib'

// Import proxy mock to enable API mocking
import '../../mocks/proxy.mock'
// Also Mock authStore & sse
import '../../mocks/authStore.mock'
import '../../mocks/sse.mock'

// Import chat store to ensure it's available
import '../../../src/store/chatStore'

import { useProjectStore } from '../../../src/store/projectStore'
import { mockFetchEventSource } from '../../mocks/sse.mock'

// Mock electron IPC
(global as any).ipcRenderer = {
  invoke: vi.fn((channel) => {
    if (channel === 'get-system-language') return Promise.resolve('en')
    if (channel === 'get-browser-port') return Promise.resolve(9222)
    if (channel === 'get-env-path') return Promise.resolve('/path/to/env')
    if (channel === 'mcp-list') return Promise.resolve({})
    if (channel === 'get-file-list') return Promise.resolve([])
    return Promise.resolve()
  }),
}

describe('Integration Test: Case 1 - New Project', () => {
  beforeEach(() => {
    vi.clearAllMocks();
    const { result } = renderHook(() => useProjectStore());
    //Reset projectStore
    result.current.getAllProjects().forEach(project => {
      result.current.removeProject(project.id)
    })
  })

  afterEach(() => {
    vi.clearAllMocks()
  })

  it('should create project with initial chatStore and task', async () => {
    const { result, rerender } = renderHook(() => useProjectStore())

    await act(async () => {
      // Step 1: Create new project
      const projectId = result.current.createProject(
        'My First Project',
        'A test project'
      )

      // Force a re-render to ensure state is updated
      rerender()

      // Debug: Log the store state immediately after creation
      console.log('Created projectId:', projectId)
      
      // Try to get project before asserting
      const debugProject = result.current.getProjectById(projectId)
      console.log('Retrieved project:', debugProject)

      // Verify project created
      expect(projectId).toBeDefined()
      
      // First check: activeProjectId should be set
      expect(debugProject?.id).toBe(projectId)

      const project = result.current.getProjectById(projectId)
      console.log('Retrieved project again:', project)
      expect(project).toBeDefined()
      expect(project?.name).toBe('My First Project')
      expect(project?.description).toBe('A test project')
    })
  })

  it('should automatically create initial chatStore in new project', () => {
    const { result } = renderHook(() => useProjectStore())

    act(() => {
      const projectId = result.current.createProject('Test Project')

      // Step 2: Verify chatStore created automatically
      const chatStore = result.current.getActiveChatStore(projectId)
      expect(chatStore).toBeDefined()

      // Verify initial task exists
      const chatState = chatStore!.getState()
      expect(chatState.activeTaskId).toBeDefined()
      expect(chatState.tasks[chatState.activeTaskId!]).toBeDefined()
    })
  })

  it('should have correct initial task state', () => {
    const { result } = renderHook(() => useProjectStore())

    act(() => {
      const projectId = result.current.createProject('Test Project')
      const chatStore = result.current.getActiveChatStore(projectId)!
      const chatState = chatStore.getState()
      const taskId = chatState.activeTaskId!
      const task = chatState.tasks[taskId]

      // Verify task initial state
      expect(task.status).toBe('pending')
      expect(task.messages).toEqual([])
      expect(task.tokens).toBe(0)
      expect(task.isPending).toBe(false)
      expect(task.hasMessages).toBe(false)
    })
  })

  it('should add user message to task', () => {
    const { result } = renderHook(() => useProjectStore())

    act(() => {
      const projectId = result.current.createProject('Test Project')
      const chatStore = result.current.getActiveChatStore(projectId)!
      const taskId = chatStore.getState().activeTaskId!

      // Step 3: User sends message
      const userMessage = {
        id: generateUniqueId(),
        role: 'user' as const,
        content: 'Create a todo app with React',
        attaches: [],
      }

      chatStore.getState().addMessages(taskId, userMessage)
      chatStore.getState().setHasMessages(taskId, true)

      // Verify message added
      const task = chatStore.getState().tasks[taskId]
      expect(task.messages).toHaveLength(1)
      expect(task.messages[0].content).toBe('Create a todo app with React')
      expect(task.hasMessages).toBe(true)
    })
  })

  it('should create historyId after starting task', async () => {
    const { result } = renderHook(() => useProjectStore())
    
    await act(async () => {
      const projectId = result.current.createProject('Test Project')
      const chatStore = result.current.getActiveChatStore(projectId)!
      const taskId = chatStore.getState().activeTaskId!

      // Add message
      chatStore.getState().addMessages(taskId, {
        id: generateUniqueId(),
        role: 'user',
        content: 'Test message',
      })

      // Mock SSE to immediately close (simulating startTask)
      mockFetchEventSource.mockImplementation((url: string, options: any) => {
        // Call onopen
        if (options.onopen) {
          options.onopen({ ok: true, status: 200 })
        }
        return Promise.resolve()
      })

      // Step 4: Start task
      await chatStore.getState().startTask(taskId)

      // Wait for historyId to be set
      await waitFor(() => {
        const historyId = result.current.getHistoryId(projectId)
        expect(historyId).toBeDefined()
        expect(historyId).toMatch(/^history-/)
      }, { timeout: 2000 })
    })
  })

  it('should handle complete user journey from project creation to task start', async () => {
    const { result } = renderHook(() => useProjectStore())

    await act(async () => {
      // Complete Flow Test
      
      // 1. Create project
      const projectId = result.current.createProject(
        'Complete Journey Test',
        'Testing full flow'
      )
      expect(projectId).toBeDefined()

      // 2. Get chatStore (automatically created)
      const chatStore = result.current.getActiveChatStore(projectId)!
      expect(chatStore).toBeDefined()

      const initiatorTaskId = chatStore.getState().activeTaskId!
      expect(initiatorTaskId).toBeDefined()

      // 3. Set user message
      const userMessage = 'Build a calculator app';

      // 4. Verify task ready to start
      const initialTask = chatStore.getState().tasks[initiatorTaskId]
      

      // 5. Mock SSE stream with to_sub_tasks event
      mockFetchEventSource.mockImplementation((url: string, options: any) => {
        setTimeout(() => {
          console.log("Sending to_sub_tasks SSE Event");
          // Simulate to_sub_tasks event
          if (options.onmessage) {
            options.onmessage({
              data: JSON.stringify({
                step: 'to_sub_tasks',
                data: {
                  summary_task: 'Calculator App|Build a simple calculator',
                  sub_tasks: [
                    { id: 'task-1', content: 'Create UI components', status: '' },
                    { id: 'task-2', content: 'Implement calculator logic', status: '' },
                  ],
                },
              }),
            })
          }
        }, 200)
      })

      // 6. Start task
      // NOTE: startTask creates a NEW chatStore and switches to it, the old chatStore is no longer active
      await chatStore.getState().startTask(initiatorTaskId, undefined, undefined, undefined, userMessage)

      // IMPORTANT: Get the NEW active chatStore after startTask creates it
      const newChatStore = result.current.getActiveChatStore()
      expect(newChatStore).toBeDefined()
      expect(newChatStore).not.toBe(chatStore) // Should be a different instance
      
      let taskId = newChatStore?.getState().activeTaskId!
      const task = newChatStore?.getState().tasks[taskId]
      expect(taskId).toBeDefined()
      if(task) {
        expect(task.hasMessages).toBe(true)
        expect(task.messages[0].content).toBe('Build a calculator app')
        expect(task.status).toBe('pending')
      }

      // 7. Wait for task breakdown
      await waitFor(() => {
        const updatedTask = newChatStore?.getState().tasks[taskId]
        expect(updatedTask?.summaryTask).toBe('Calculator App|Build a simple calculator')
        //Bcz of newTaskInfo { id: '', content: '', status: '' } we have 3 items
        expect(updatedTask?.taskInfo).toHaveLength(3)
        expect(updatedTask?.taskRunning).toHaveLength(3)
      }, { timeout: 2000 })
    })
  })

  it('should not create new project if empty project exists (optimization)', () => {
    const { result } = renderHook(() => useProjectStore())

    act(() => {
      // Create first empty project
      const projectId1 = result.current.createProject('First Project')

      // Before adding any messages, create another project
      // Should reuse the empty project
      const projectId2 = result.current.createProject('Second Project')

      // Should reuse the same project ID
      expect(projectId2).toBe(projectId1)

      // Project should be updated with new name
      const project = result.current.getProjectById(projectId2)
      expect(project?.name).toBe('Second Project')
    })
  })

  it('should create new project if existing project has messages', () => {
    const { result } = renderHook(() => useProjectStore())

    act(() => {
      // Create first project
      const projectId1 = result.current.createProject('First Project')

      // Add a message (making it non-empty)
      const chatStore = result.current.getActiveChatStore(projectId1)!
      const taskId = chatStore.getState().activeTaskId!
      chatStore.getState().addMessages(taskId, {
        id: generateUniqueId(),
        role: 'user',
        content: 'Test message',
      })

      // Now create second project
      const projectId2 = result.current.createProject('Second Project')

      // Should create new project
      expect(projectId2).not.toBe(projectId1)
      expect(result.current.getAllProjects()).toHaveLength(2)
    })
  })

  describe('Edge Cases', () => {
    it('should handle project creation with minimal data', async () => {
      const { result } = renderHook(() => useProjectStore())

      await act(async () => {
        const projectId = result.current.createProject('Minimal Project')

        // Wait a tick to ensure all state updates are complete
        await new Promise(resolve => setTimeout(resolve, 0))

        const project = result.current.getProjectById(projectId)
        expect(project?.name).toBe('Minimal Project')
        expect(project?.description).toBeUndefined()
      })
    })

    it('should handle empty message gracefully', async () => {
      const { result } = renderHook(() => useProjectStore())

      await act(async () => {
        const projectId = result.current.createProject('Test Project')
        
        // Wait a tick for project creation to complete
        await new Promise(resolve => setTimeout(resolve, 0))
        
        const chatStore = result.current.getActiveChatStore(projectId)!
        const taskId = chatStore.getState().activeTaskId!

        // Add empty message
        chatStore.getState().addMessages(taskId, {
          id: generateUniqueId(),
          role: 'user',
          content: '',
        })

        // Wait for message update to complete
        await new Promise(resolve => setTimeout(resolve, 0))

        const task = chatStore.getState().tasks[taskId]
        expect(task.messages).toHaveLength(1)
        expect(task.messages[0].content).toBe('')
      })
    })

    it('should handle rapid project creation', async () => {
      const { result } = renderHook(() => useProjectStore())

      await act(async () => {
        const projectIds = []
        for (let i = 0; i < 5; i++) {
          projectIds.push(result.current.createProject(`Project ${i}`))
          // Add small delay between each creation to ensure proper state updates
          await new Promise(resolve => setTimeout(resolve, 0))
        }

        // Only first should be created, rest reuse until messages added
        expect(new Set(projectIds).size).toBeLessThanOrEqual(1)
      })
    })
  })
})
