/**
 * MockAdapter Tests
 * Verifies response queue, call counting, and backward compatibility
 */

import { describe, it, expect } from 'vitest'
import { MockAdapter, type MockAdapterConfig } from '../mock-adapter'

describe('MockAdapter', () => {
  // Helper schemas for testing
  const agentIterationSchema = {
    type: 'object',
    properties: {
      reasoning: { type: 'string' },
      memoryOps: { type: 'object' },
      nextActions: { type: 'array' }
    }
  }

  const completionSchema = {
    type: 'object',
    properties: {
      response: { type: 'string' },
      processedAt: { type: 'string' }
    }
  }

  const customCompletionSchema = {
    type: 'object',
    properties: {
      result: { type: 'string' },
      metadata: { type: 'object' }
    }
  }

  const messages = [
    { role: 'system' as const, content: 'test' },
    { role: 'user' as const, content: 'test' }
  ]

  describe('Backward Compatibility', () => {
    it('default constructor returns immediate completion for agent iterations', async () => {
      const adapter = new MockAdapter()

      const response = await adapter.generate({
        messages: [
          { role: 'system', content: 'test' },
          { role: 'user', content: 'test' }
        ],
        responseSchema: {
          type: 'object',
          properties: {
            reasoning: { type: 'string' },
            nextActions: { type: 'array' }
          }
        }
      })

      expect(response.output).toEqual({
        reasoning: 'Mock reasoning',
        memoryOps: {},
        nextActions: [{ type: 'complete' }]
      })
    })

    it('default constructor returns generic output for completion phase', async () => {
      const adapter = new MockAdapter()

      const response = await adapter.generate({
        messages: [
          { role: 'system', content: 'test' },
          { role: 'user', content: 'test' }
        ],
        responseSchema: {
          type: 'object',
          properties: {
            response: { type: 'string' },
            processedAt: { type: 'string' }
          }
        }
      })

      expect(response.output).toHaveProperty('response', 'Mock response')
      expect(response.output).toHaveProperty('processedAt')
      expect(typeof response.output.processedAt).toBe('string')
    })
  })

  describe('Response Queue', () => {
    it('returns queued responses in order', async () => {
      const config: MockAdapterConfig = {
        responses: [
          {
            reasoning: 'First iteration',
            nextActions: [{ type: 'tool-call', id: '1', name: 'tool1', input: {} }]
          },
          {
            reasoning: 'Second iteration',
            nextActions: [{ type: 'tool-call', id: '2', name: 'tool2', input: {} }]
          },
          {
            reasoning: 'Final iteration',
            nextActions: [{ type: 'complete' }]
          }
        ]
      }

      const adapter = new MockAdapter(config)

      const agentSchema = {
        type: 'object',
        properties: {
          reasoning: { type: 'string' },
          nextActions: { type: 'array' }
        }
      }

      // First call
      const response1 = await adapter.generate({
        messages: [
          { role: 'system', content: 'test' },
          { role: 'user', content: 'test' }
        ],
        responseSchema: agentSchema
      })
      expect(response1.output.reasoning).toBe('First iteration')
      expect(response1.output.nextActions).toHaveLength(1)
      expect(response1.output.nextActions[0].type).toBe('tool-call')

      // Second call
      const response2 = await adapter.generate({
        messages: [
          { role: 'system', content: 'test' },
          { role: 'user', content: 'test' }
        ],
        responseSchema: agentSchema
      })
      expect(response2.output.reasoning).toBe('Second iteration')
      expect(response2.output.nextActions[0].type).toBe('tool-call')

      // Third call
      const response3 = await adapter.generate({
        messages: [
          { role: 'system', content: 'test' },
          { role: 'user', content: 'test' }
        ],
        responseSchema: agentSchema
      })
      expect(response3.output.reasoning).toBe('Final iteration')
      expect(response3.output.nextActions[0].type).toBe('complete')
    })

    it('falls back to default response when queue is empty', async () => {
      const config: MockAdapterConfig = {
        responses: [
          {
            reasoning: 'Single iteration',
            nextActions: [{ type: 'tool-call', id: '1', name: 'tool1', input: {} }]
          }
        ]
      }

      const adapter = new MockAdapter(config)

      // First call uses queued response
      const response1 = await adapter.generate({
        messages,
        responseSchema: agentIterationSchema
      })
      expect(response1.output.reasoning).toBe('Single iteration')

      // Second call uses default (queue empty)
      const response2 = await adapter.generate({
        messages,
        responseSchema: agentIterationSchema
      })
      expect(response2.output.reasoning).toBe('Mock reasoning')
      expect(response2.output.nextActions).toEqual([{ type: 'complete' }])
    })

    it('supports partial response objects with defaults', async () => {
      const config: MockAdapterConfig = {
        responses: [
          {
            // Only reasoning provided, should use defaults for memoryOps and nextActions
            reasoning: 'Custom reasoning only'
          }
        ]
      }

      const adapter = new MockAdapter(config)

      const response = await adapter.generate({
        messages,
        responseSchema: agentIterationSchema
      })

      expect(response.output.reasoning).toBe('Custom reasoning only')
      expect(response.output.memoryOps).toEqual({})
      expect(response.output.nextActions).toEqual([{ type: 'complete' }])
    })
  })

  describe('Call Count Tracking', () => {
    it('tracks number of generate calls', async () => {
      const adapter = new MockAdapter()

      expect(adapter.getCallCount()).toBe(0)

      await adapter.generate({
        messages,
        responseSchema: agentIterationSchema
      })
      expect(adapter.getCallCount()).toBe(1)

      await adapter.generate({
        messages,
        responseSchema: agentIterationSchema
      })
      expect(adapter.getCallCount()).toBe(2)

      await adapter.generate({
        messages,
        responseSchema: agentIterationSchema
      })
      expect(adapter.getCallCount()).toBe(3)
    })

    it('counts both iteration and completion calls', async () => {
      const adapter = new MockAdapter()

      // Agent iteration call
      await adapter.generate({
        messages,
        responseSchema: agentIterationSchema
      })

      // Completion call
      await adapter.generate({
        messages,
        responseSchema: completionSchema
      })

      expect(adapter.getCallCount()).toBe(2)
    })
  })

  describe('Token Usage Simulation', () => {
    it('returns incremental token usage for agent iterations', async () => {
      const adapter = new MockAdapter()

      const response1 = await adapter.generate({
        messages,
        responseSchema: agentIterationSchema
      })
      expect(response1.usage).toEqual({
        inputTokens: 100,
        outputTokens: 50,
        totalTokens: 150
      })

      const response2 = await adapter.generate({
        messages,
        responseSchema: agentIterationSchema
      })
      expect(response2.usage).toEqual({
        inputTokens: 200,
        outputTokens: 100,
        totalTokens: 300
      })

      const response3 = await adapter.generate({
        messages,
        responseSchema: agentIterationSchema
      })
      expect(response3.usage).toEqual({
        inputTokens: 300,
        outputTokens: 150,
        totalTokens: 450
      })
    })

    it('returns fixed token usage for completion phase', async () => {
      const adapter = new MockAdapter()

      const response = await adapter.generate({
        messages,
        responseSchema: completionSchema
      })

      expect(response.usage).toEqual({
        inputTokens: 50,
        outputTokens: 25,
        totalTokens: 75
      })
    })
  })

  describe('Custom Completion Output', () => {
    it('uses custom completion output when provided', async () => {
      const customOutput = {
        result: 'Custom completion result',
        metadata: { processed: true }
      }

      const config: MockAdapterConfig = {
        completionOutput: customOutput
      }

      const adapter = new MockAdapter(config)

      const response = await adapter.generate({
        messages,
        responseSchema: customCompletionSchema
      })

      expect(response.output).toEqual(customOutput)
    })
  })

  describe('Memory Operations', () => {
    it('includes memory operations in queued responses', async () => {
      const config: MockAdapterConfig = {
        responses: [
          {
            reasoning: 'Setting memory',
            memoryOps: {
              set: { key1: 'value1', key2: 'value2' },
              delete: ['oldKey']
            },
            nextActions: [{ type: 'complete' }]
          }
        ]
      }

      const adapter = new MockAdapter(config)

      const response = await adapter.generate({
        messages,
        responseSchema: agentIterationSchema
      })

      expect(response.output.memoryOps).toEqual({
        set: { key1: 'value1', key2: 'value2' },
        delete: ['oldKey']
      })
    })
  })

  describe('Multi-Iteration Scenario', () => {
    it('simulates complete agent execution with multiple iterations', async () => {
      const config: MockAdapterConfig = {
        responses: [
          {
            reasoning: 'Need to call search tool',
            nextActions: [
              {
                type: 'tool-call',
                id: '1',
                name: 'search',
                input: { query: 'test' }
              }
            ]
          },
          {
            reasoning: 'Need to call process tool',
            memoryOps: {
              set: { searchResult: 'found data' }
            },
            nextActions: [
              {
                type: 'tool-call',
                id: '2',
                name: 'process',
                input: { data: 'found data' }
              }
            ]
          },
          {
            reasoning: 'All tasks complete',
            nextActions: [{ type: 'complete' }]
          }
        ],
        completionOutput: {
          response: 'Processing complete',
          processedAt: '2025-01-01T00:00:00Z'
        }
      }

      const adapter = new MockAdapter(config)

      // Iteration 1: Search tool call
      const iter1 = await adapter.generate({
        messages,
        responseSchema: agentIterationSchema
      })
      expect(iter1.output.reasoning).toBe('Need to call search tool')
      expect(iter1.output.nextActions[0].type).toBe('tool-call')
      expect(iter1.output.nextActions[0].name).toBe('search')

      // Iteration 2: Process tool call with memory update
      const iter2 = await adapter.generate({
        messages,
        responseSchema: agentIterationSchema
      })
      expect(iter2.output.reasoning).toBe('Need to call process tool')
      expect(iter2.output.memoryOps).toEqual({ set: { searchResult: 'found data' } })
      expect(iter2.output.nextActions[0].name).toBe('process')

      // Iteration 3: Completion
      const iter3 = await adapter.generate({
        messages,
        responseSchema: agentIterationSchema
      })
      expect(iter3.output.reasoning).toBe('All tasks complete')
      expect(iter3.output.nextActions[0].type).toBe('complete')

      // Completion phase
      const completion = await adapter.generate({
        messages,
        responseSchema: completionSchema
      })
      expect(completion.output).toEqual({
        response: 'Processing complete',
        processedAt: '2025-01-01T00:00:00Z'
      })

      // Verify total calls
      expect(adapter.getCallCount()).toBe(4)
    })
  })
})
