// ========= 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. =========

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 useChatStoreAdapter from '../../../src/hooks/useChatStoreAdapter'
import { mockFetchEventSource } from '../../mocks/sse.mock'

// Helper function for sequential SSE events
const createSSESequence = (events: Array<{ event: any; delay: number }>) => {
  return async (onMessage: (data: any) => void) => {
    for (let i = 0; i < events.length; i++) {
      const { event, delay } = events[i]
      
      await new Promise<void>((resolve) => {
        setTimeout(() => {
          console.log(`Sending SSE Event ${i + 1}:`, event.step);
          onMessage({
            data: JSON.stringify(event)
          })
          resolve()
        }, delay)
      })
    }
  }
}

// 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()
  }),
}

// Mock window.electronAPI
Object.defineProperty(window, 'electronAPI', {
  value: {
    uploadLog: vi.fn().mockResolvedValue(undefined),
    // Add other electronAPI methods as needed
  },
  writable: true,
})


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

    //Create initial Project
    const projectId = result.current.createProject(
      'Complete Journey Test',
      'Testing full flow'
    )
    expect(projectId).toBeDefined()

    // 2. Get chatStore (automatically created)
    let chatStore = result.current.getActiveChatStore(projectId)!
    expect(chatStore).toBeDefined()
    const initiatorTaskId = chatStore.getState().activeTaskId!
    expect(initiatorTaskId).toBeDefined()
  })

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

  it("Sequential startTask appends chatStores to same project", async () => {
    const { result, rerender } = renderHook(() => useChatStoreAdapter())
    
    // Setup the events sequence
    const eventSequence = createSSESequence([
      {
        event: {
          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: '' },
            ],
          },
        },
        delay: 100
      },
      {
        event: {
          step: "end", 
          data: "--- Subtask 1760647257350-6021.1 Result ---\nI'm doing well, thank you for asking! How are you today?"
        },
        delay: 400
      }
    ])

    // Mock SSE stream with controlled events
    mockFetchEventSource.mockImplementation(async (url: string, options: any) => {
      if (options.onmessage) {
        await eventSequence(options.onmessage)
      }
    })

    await act(async () => {
      // Complete Flow Test
      const {chatStore, projectStore} = result.current
      const initiatorTaskId = chatStore.activeTaskId;

      //User Message to send
      const userMessage = 'Build a calculator app';

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

      //Rerender to get the latest chatStore
      rerender()
    })

    // Test 1: Check initial task creation and first SSE event (to_sub_tasks)
    await waitFor(() => {
      rerender()
      const {chatStore: newChatStore, projectStore} = result.current;
      expect(newChatStore).toBeDefined()
      
      let taskId = newChatStore?.activeTaskId!
      const task = newChatStore?.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')
      }

      const updatedTask = newChatStore?.tasks[taskId]
      expect(updatedTask?.summaryTask).toBe('Calculator App|Build a simple calculator')
      expect(updatedTask?.taskInfo).toHaveLength(3)
      expect(updatedTask?.taskRunning).toHaveLength(3)

      //Two chatStores - first initial
      expect(projectStore.getAllChatStores(projectStore.activeProjectId)).toHaveLength(2)
    }, { timeout: 1000 })

    // Test 2: Check progress event has been processed 
    await waitFor(() => {
      rerender()
      const {chatStore: newChatStore} = result.current;
      let taskId = newChatStore?.activeTaskId!
      const task = newChatStore?.tasks[taskId]
      
      // Check if progress message exists in messages or status updates
      // Adjust this based on how your app handles progress events
      expect(task).toBeDefined()
      console.log("Progress test - task status:", task?.status);
    }, { timeout: 1500 })

    // Test 3: Rerender until status is "finished"
    await waitFor(() => {
      rerender()
      const {chatStore: newChatStore} = result.current;
      let taskId = newChatStore?.activeTaskId!
      const task = newChatStore?.tasks[taskId]
      
      if(task) {
        // Check if task is completed or has final result
        // Adjust these assertions based on your app's behavior
        console.log("End test - task status:", task?.status);
        console.log("End test - task messages:", task?.messages?.length);
      }
      expect(task.status).toBe("finished")
      console.log(task);
    }, { timeout: 2000 });

    //Before starting new chatStore
    const { chatStore, projectStore } = result.current;
    expect(Object.keys(chatStore.tasks)).toHaveLength(1);
    //Initial ChatStore + appendedOne
    expect(projectStore.getAllChatStores(projectStore.activeProjectId)).toHaveLength(2)
    //Make all tasks are skipped after end
    chatStore.tasks[chatStore.activeTaskId].taskRunning.forEach((task:any) => {
      expect(task.status).toBe("skipped")
    })
    chatStore.tasks[chatStore.activeTaskId].taskInfo.forEach((task:any) => {
      expect(task.status).toBe("skipped")
    })

    



    // Test: Start second chat session with different events
    await act(async () => {
      rerender()
      const {chatStore, projectStore} = result.current
      const initiatorTaskId = chatStore.activeTaskId;

      // Setup different events for second session
      const secondEventSequence = createSSESequence([
        {
          event: {
            "step": "confirmed", 
            "data": {"question": "how are you?"}
          },
          delay: 100
        },
        {
          event: {
            step: 'to_sub_tasks',
            data: {
              summary_task: 'Todo App|Build a todo application',
              sub_tasks: [
                { id: 'task-3', content: 'Design todo interface', status: '' },
                { id: 'task-4', content: 'Implement todo logic', status: '' },
              ],
            },
          },
          delay: 200
        },
        {
          event: {
            step: "end", 
            data: "--- Second Task Result ---\nTodo app planning completed!"
          },
          delay: 300
        },
        {
          event: {
            step: "end", 
            data: "--- Subtask 1760647257350-6021.1 Result ---\nI'm doing well, thank you for asking! How are you today?"
          },
          delay: 400
        }
      ])

      // Update the mock for the second call
      mockFetchEventSource.mockImplementation(async (url: string, options: any) => {
        if (options.onmessage) {
          await secondEventSequence(options.onmessage)
        }
      })

      const userMessage = 'Build a todo app';
      await chatStore.startTask(initiatorTaskId, undefined, undefined, undefined, userMessage)
      rerender()
    })

    // Test the second session results
    await waitFor(() => {
      rerender()
      const {chatStore: newChatStore, projectStore} = result.current;
      let taskId = newChatStore?.activeTaskId!
      const task = newChatStore?.tasks[taskId]
      
      if(task) {
        expect(task.messages[0].content).toBe('Build a todo app')
        // Check if the new summary task is set correctly
        expect(task.summaryTask).toBe('Todo App|Build a todo application')
      }
      
      // Should now have 3 chat stores (initial + 2 task sessions)
      expect(projectStore.getAllChatStores(projectStore.activeProjectId)).toHaveLength(3)
    }, { timeout: 1500 })

    // CHECK POST End State of Run 2
    await waitFor(() => {
      rerender()
      const {chatStore: newChatStore} = result.current;
      let taskId = newChatStore?.activeTaskId!
      const task = newChatStore?.tasks[taskId]
      
      if(task) {
        // Check if task is completed or has final result
        // Adjust these assertions based on your app's behavior
        console.log("End test 2 - task status:", task?.status);
        console.log("End test 2 - task messages:", task?.messages?.length);
      }
      expect(task.status).toBe("finished")
      console.log(task);
    }, { timeout: 2000 });

    //Before starting new chatStore
    const { chatStore:secondChatStore } = result.current;
    expect(Object.keys(secondChatStore.tasks)).toHaveLength(1);
    //Initial ChatStore + appendedOne
    expect(projectStore.getAllChatStores(projectStore.activeProjectId)).toHaveLength(3)
    //Make all tasks are skipped after end
    secondChatStore.tasks[secondChatStore.activeTaskId].taskRunning.forEach((task:any) => {
      expect(task.status).toBe("skipped")
    })
    secondChatStore.tasks[secondChatStore.activeTaskId].taskInfo.forEach((task:any) => {
      expect(task.status).toBe("skipped")
    })
  })

  it("should handle individual SSE events with precise timing", async () => {
    const { result, rerender } = renderHook(() => useChatStoreAdapter())
    
    let messageCallback: ((data: any) => void) | null = null
    
    // Mock SSE to capture the callback for manual event triggering
    mockFetchEventSource.mockImplementation((url: string, options: any) => {
      messageCallback = options.onmessage
      // Don't send any events automatically
    })

    // Start the task
    await act(async () => {
      const {chatStore} = result.current
      const initiatorTaskId = chatStore.activeTaskId;
      await chatStore.startTask(initiatorTaskId, undefined, undefined, undefined, 'Test manual events')
      rerender()
    })

    // Manually send first event and test result
    await act(async () => {
      messageCallback?.({
        data: JSON.stringify({
          step: 'to_sub_tasks',
          data: {
            summary_task: 'Manual Test|Testing manual event control',
            sub_tasks: [{ id: 'manual-1', content: 'Manual task 1', status: '' }],
          },
        })
      })
    })

    // Test first event result
    await waitFor(() => {
      rerender()
      const {chatStore} = result.current;
      let taskId = chatStore?.activeTaskId!
      const task = chatStore?.tasks[taskId]
      expect(task?.summaryTask).toBe('Manual Test|Testing manual event control')
    })

    // Send second event and test result
    await act(async () => {
      messageCallback?.({
        data: JSON.stringify({
          step: 'progress',
          data: 'Manual progress update'
        })
      })
    })

    // Test second event result
    await waitFor(() => {
      rerender()
      const {chatStore} = result.current;
      let taskId = chatStore?.activeTaskId!
      const task = chatStore?.tasks[taskId]
      // Add your specific assertions for progress events here
      expect(task).toBeDefined()
    })

    // Send final event
    await act(async () => {
      messageCallback?.({
        data: JSON.stringify({
          step: 'end',
          data: 'Manual test completed successfully'
        })
      })
    })

    // Test final state
    await waitFor(() => {
      rerender()
      const {chatStore} = result.current;
      let taskId = chatStore?.activeTaskId!
      const task = chatStore?.tasks[taskId]
      // Add your specific assertions for end state here
      expect(task).toBeDefined()
    })
  })

  //TODO: Don't let new startTask until newChatStore appended
  it("Parallel startTask calls with separate chatStores (startTask -> wait for append -> startTask)", async () => {
    const { result, rerender } = renderHook(() => useChatStoreAdapter())
    
    let sseCallCount = 0
    let firstTaskChatStore: any = null
    let secondTaskChatStore: any = null
    
    // Setup SSE events for the first task
    const firstTaskEventSequence = createSSESequence([
      {
        event: {
          step: 'to_sub_tasks',
          data: {
            summary_task: 'First Task|Build a calculator app',
            sub_tasks: [
              { id: 'first-1', content: 'Create calculator UI', status: '' },
              { id: 'first-2', content: 'Implement calc logic', status: '' },
            ],
          },
        },
        delay: 100
      },
      {
        event: {
          step: "end", 
          data: "--- First Task Result ---\nCalculator app completed successfully!"
        },
        delay: 300
      }
    ])

    // Setup SSE events for the second task
    const secondTaskEventSequence = createSSESequence([
      {
        event: {
          step: 'to_sub_tasks',
          data: {
            summary_task: 'Second Task|Build a todo app',
            sub_tasks: [
              { id: 'second-1', content: 'Create todo UI', status: '' },
              { id: 'second-2', content: 'Implement todo logic', status: '' },
            ],
          },
        },
        delay: 100
      },
      {
        event: {
          step: "end", 
          data: "--- Second Task Result ---\nTodo app completed successfully!"
        },
        delay: 300
      }
    ])

    // Mock SSE to handle sequential calls with different events
    mockFetchEventSource.mockImplementation(async (url: string, options: any) => {
      sseCallCount++
      console.log(`SSE Call #${sseCallCount} initiated`)
      
      if (sseCallCount === 1) {
        // First task gets first event sequence
        console.log('Processing first task events')
        if (options.onmessage) {
          await firstTaskEventSequence(options.onmessage)
        }
      } else if (sseCallCount === 2) {
        // Second task gets second event sequence
        console.log('Processing second task events')
        if (options.onmessage) {
          await secondTaskEventSequence(options.onmessage)
        }
      }
    })

    // Get initial chatStore reference
    const { chatStore: initialChatStore, projectStore } = result.current
    const initialChatStoreRef = initialChatStore
    const initiatorTaskId = initialChatStore.activeTaskId

    // Step 1: Start first task
    await act(async () => {
      const userMessage1 = 'Build a calculator app'
      await initialChatStore.startTask(initiatorTaskId, undefined, undefined, undefined, userMessage1)
      rerender()
    })

    // Verify first task started and chatStore was created
    await waitFor(() => {
      rerender()
      const { chatStore: currentChatStore, projectStore } = result.current
      
      // Should have 2 chatStores: initial + first task
      const allChatStores = projectStore.getAllChatStores(projectStore.activeProjectId)
      expect(allChatStores).toHaveLength(2)
      
      // Current chatStore should be different from initial (new one created)
      expect(currentChatStore).not.toBe(initialChatStoreRef)
      firstTaskChatStore = currentChatStore
      
      // Verify first task details
      const activeTaskId = currentChatStore.activeTaskId
      const activeTask = currentChatStore.tasks[activeTaskId]
      expect(activeTask).toBeDefined()
      expect(activeTask.hasMessages).toBe(true)
      expect(activeTask.messages[0].content).toBe('Build a calculator app')
      
    }, { timeout: 1000 })

    // Wait for first task SSE events to process (summary_task)
    await waitFor(() => {
      rerender()
      const { chatStore: currentChatStore } = result.current
      const activeTaskId = currentChatStore.activeTaskId
      const activeTask = currentChatStore.tasks[activeTaskId]
      
      // Check that SSE events have been processed
      expect(activeTask.summaryTask).toBe('First Task|Build a calculator app')
      expect(activeTask.taskInfo).toHaveLength(3) // main task + 2 subtasks
      expect(activeTask.taskRunning).toHaveLength(3)
      
      console.log("First task SSE events processed")
    }, { timeout: 1500 })

    // Wait for first task to complete
    await waitFor(() => {
      rerender()
      const { chatStore: currentChatStore } = result.current
      const activeTaskId = currentChatStore.activeTaskId
      const activeTask = currentChatStore.tasks[activeTaskId]
      
      expect(activeTask.status).toBe("finished")
      
      console.log("First task completed")
    }, { timeout: 2000 })

    // Step 2: Wait for project append to complete before starting second task
    await waitFor(() => {
      rerender()
      const { projectStore } = result.current
      
      // Ensure the project has been properly updated with the appended first chatStore
      const allChatStores = projectStore.getAllChatStores(projectStore.activeProjectId)
      expect(allChatStores).toHaveLength(2)
      
      // Verify the active chatStore is properly set and ready for next task
      const activeChatStore = projectStore.getActiveChatStore(projectStore.activeProjectId)
      expect(activeChatStore).toBeDefined()
      expect(activeChatStore.getState()?.activeTaskId).toBeDefined()
      
      console.log("Project append completed, ready for second task")
    }, { timeout: 1000 })

    // Step 3: Start second task on the same chatStore
    await act(async () => {
      rerender()
      const { chatStore: currentChatStore } = result.current
      const currentInitiatorTaskId = currentChatStore.activeTaskId
      
      const userMessage2 = 'Build a todo app'
      await currentChatStore.startTask(currentInitiatorTaskId, undefined, undefined, undefined, userMessage2)
      rerender()
    })

    // Verify second task started and new chatStore was created
    await waitFor(() => {
      rerender()
      const { chatStore: currentChatStore, projectStore } = result.current
      
      // Should now have 3 chatStores: initial + first task + second task
      const allChatStores = projectStore.getAllChatStores(projectStore.activeProjectId)
      expect(allChatStores).toHaveLength(3)
      
      // Current chatStore should be different from first task chatStore
      expect(currentChatStore).not.toBe(firstTaskChatStore)
      secondTaskChatStore = currentChatStore
      
      // Verify second task details
      const activeTaskId = currentChatStore.activeTaskId
      const activeTask = currentChatStore.tasks[activeTaskId]
      expect(activeTask).toBeDefined()
      expect(activeTask.hasMessages).toBe(true)
      expect(activeTask.messages[0].content).toBe('Build a todo app')
      
    }, { timeout: 1000 })

    // Wait for second task SSE events to process (summary_task)
    await waitFor(() => {
      rerender()
      const { chatStore: currentChatStore } = result.current
      const activeTaskId = currentChatStore.activeTaskId
      const activeTask = currentChatStore.tasks[activeTaskId]
      
      // Check that SSE events have been processed
      expect(activeTask.summaryTask).toBe('Second Task|Build a todo app')
      expect(activeTask.taskInfo).toHaveLength(3) // main task + 2 subtasks
      expect(activeTask.taskRunning).toHaveLength(3)
      
      console.log("Second task SSE events processed")
    }, { timeout: 1500 })

    // Wait for second task to complete
    await waitFor(() => {
      rerender()
      const { chatStore: currentChatStore } = result.current
      const activeTaskId = currentChatStore.activeTaskId
      const activeTask = currentChatStore.tasks[activeTaskId]
      
      expect(activeTask.status).toBe("finished")
      
      console.log("Second task completed")
    }, { timeout: 2000 })

    // Final verification: Both chatStores should have separate states
    const { projectStore: finalProjectStore } = result.current
    const allFinalChatStores = finalProjectStore.getAllChatStores(finalProjectStore.activeProjectId)
    
    // Verify we have 3 separate chatStores with their own states
    expect(allFinalChatStores).toHaveLength(3)
    expect(sseCallCount).toBe(2) // Two separate SSE calls
    
    // Verify each chatStore has its own task with different content
    expect(firstTaskChatStore).not.toBe(secondTaskChatStore)
    
    // Get the current state of chatStores from the project store to verify final states
    const [initialChatStoreFromProject, firstChatStoreFromProject, secondChatStoreFromProject] = allFinalChatStores
    
    // Verify first chatStore state (should be the second in the array after initial)
    const firstTaskId = firstChatStoreFromProject.chatStore.getState().activeTaskId
    const firstTask = firstChatStoreFromProject.chatStore.getState().tasks[firstTaskId]
    expect(firstTask.messages[0].content).toBe('Build a calculator app')
    expect(firstTask.summaryTask).toBe('First Task|Build a calculator app')
    
    // Verify second chatStore state (should be the third in the array)
    const secondTaskId = secondChatStoreFromProject.chatStore.getState().activeTaskId
    const secondTask = secondChatStoreFromProject.chatStore.getState().tasks[secondTaskId]
    expect(secondTask.messages[0].content).toBe('Build a todo app')
    expect(secondTask.summaryTask).toBe('Second Task|Build a todo app')
    
    console.log('Sequential startTask test with separate chatStores completed successfully')
  })
})