import { describe, it, expect, vi, beforeEach } from 'vitest'
import { createPdfRenderTool, PdfRenderInputSchema } from '../pdf'
import { ToolingError } from '../../types'
import { getToolServices } from '../../registry'
import { createMockExecutionContext } from '../../../test-utils/mocks'

vi.mock('../../registry', () => ({
  getToolServices: vi.fn()
}))

describe('createPdfRenderTool', () => {
  const mockConfig = {
    name: 'render_pdf',
    description: 'Render PDF documents for proposals'
  }

  const mockContext = createMockExecutionContext({
    organizationId: 'org-123',
    executionId: 'exec-456',
    resourceId: 'agent-789'
  })

  const mockPdfService = {
    render: vi.fn().mockResolvedValue({
      success: true,
      pdfUrl: 'https://storage.example.com/proposals/deal-123/proposal.pdf',
      size: 15000
    })
  }

  beforeEach(() => {
    vi.clearAllMocks()
    mockPdfService.render.mockResolvedValue({
      success: true,
      pdfUrl: 'https://storage.example.com/proposals/deal-123/proposal.pdf',
      size: 15000
    })
    vi.mocked(getToolServices).mockReturnValue({
      commandQueueService: null,
      taskSchedulerService: null,
      notificationsService: null,
      emailService: null,
      credentialsService: null,
      integrationService: null,
      leadService: null,
      storageService: null,
      pdfService: mockPdfService
    })
  })

  describe('tool creation', () => {
    it('creates tool with correct name and description', () => {
      const tool = createPdfRenderTool(mockConfig)

      expect(tool.name).toBe('render_pdf')
      expect(tool.description).toBe('Render PDF documents for proposals')
    })

    it('uses PdfRenderInputSchema as inputSchema', () => {
      const tool = createPdfRenderTool(mockConfig)

      expect(tool.inputSchema).toBe(PdfRenderInputSchema)
    })
  })

  describe('execute with valid input', () => {
    it('renders PDF with minimal document structure', async () => {
      const tool = createPdfRenderTool(mockConfig)
      const input = {
        document: {
          pages: [{
            sections: [
              { type: 'text', content: 'Proposal Title', variant: 'title' }
            ]
          }]
        },
        storage: {
          bucket: 'acquisition',
          path: 'proposals/deal-123/proposal.pdf'
        }
      }

      const result = await tool.execute({
        input,
        executionContext: mockContext
      })

      expect(result).toEqual({
        success: true,
        pdfUrl: 'https://storage.example.com/proposals/deal-123/proposal.pdf',
        size: 15000
      })
      expect(mockPdfService.render).toHaveBeenCalledWith(
        expect.objectContaining({
          organizationId: 'org-123',
          storage: input.storage
        })
      )
      // Verify document was passed (may have defaults applied by schema)
      expect(mockPdfService.render).toHaveBeenCalledWith(
        expect.objectContaining({
          document: expect.objectContaining({
            pages: expect.arrayContaining([
              expect.objectContaining({
                sections: input.document.pages[0].sections
              })
            ])
          })
        })
      )
    })

    it('renders PDF with full document structure', async () => {
      const tool = createPdfRenderTool(mockConfig)
      const input = {
        document: {
          pages: [{
            sections: [
              { type: 'text', content: 'Proposal', variant: 'title' },
              { type: 'metric', value: '$50,000', label: 'Project Value' },
              { type: 'list', items: ['Item 1', 'Item 2'], ordered: true },
              {
                type: 'card',
                title: 'Details',
                variant: 'accent',
                content: [
                  { type: 'text', content: 'Card content', variant: 'body' }
                ]
              }
            ],
            header: { title: 'Acme Corp Proposal', logo: 'https://example.com/logo.png' },
            footer: true
          }],
          metadata: {
            title: 'Business Proposal',
            author: 'Sales Team'
          }
        },
        storage: {
          bucket: 'acquisition',
          path: 'proposals/deal-456/final-proposal.pdf'
        }
      }

      await tool.execute({
        input,
        executionContext: mockContext
      })

      expect(mockPdfService.render).toHaveBeenCalledWith(
        expect.objectContaining({
          organizationId: 'org-123',
          document: input.document,
          storage: input.storage
        })
      )
    })

    it('passes theme overrides to service', async () => {
      const tool = createPdfRenderTool(mockConfig)
      const input = {
        document: {
          pages: [{
            sections: [{ type: 'text', content: 'Test' }]
          }]
        },
        theme: {
          colors: { primary: '#1a73e8' },
          spacing: { page: 40 }
        },
        storage: {
          bucket: 'acquisition',
          path: 'proposals/themed.pdf'
        }
      }

      await tool.execute({
        input,
        executionContext: mockContext
      })

      expect(mockPdfService.render).toHaveBeenCalledWith(
        expect.objectContaining({
          theme: input.theme
        })
      )
    })
  })

  describe('error handling', () => {
    it('throws Error when executionContext is missing', async () => {
      const tool = createPdfRenderTool(mockConfig)
      const input = {
        document: {
          pages: [{ sections: [{ type: 'text', content: 'Test' }] }]
        },
        storage: { bucket: 'test', path: 'test.pdf' }
      }

      await expect(tool.execute({ input })).rejects.toThrow(
        'ExecutionContext is unavailable'
      )
    })

    it('throws ToolingError when pdfService is unavailable', async () => {
      vi.mocked(getToolServices).mockReturnValue({
        commandQueueService: null,
        taskSchedulerService: null,
        notificationsService: null,
        emailService: null,
        credentialsService: null,
        integrationService: null,
        leadService: null,
        storageService: null,
        pdfService: null
      })

      const tool = createPdfRenderTool(mockConfig)
      const input = {
        document: {
          pages: [{ sections: [{ type: 'text', content: 'Test' }] }]
        },
        storage: { bucket: 'test', path: 'test.pdf' }
      }

      try {
        await tool.execute({
          input,
          executionContext: mockContext
        })
        expect.fail('Should have thrown')
      } catch (error) {
        expect(error).toBeInstanceOf(ToolingError)
        const toolingError = error as ToolingError
        expect(toolingError.errorType).toBe('service_unavailable')
        expect(toolingError.message).toBe('PdfService is unavailable')
      }
    })

    it('propagates errors from pdfService', async () => {
      mockPdfService.render.mockRejectedValue(new Error('Rendering failed'))

      const tool = createPdfRenderTool(mockConfig)
      const input = {
        document: {
          pages: [{ sections: [{ type: 'text', content: 'Test' }] }]
        },
        storage: { bucket: 'test', path: 'test.pdf' }
      }

      await expect(
        tool.execute({
          input,
          executionContext: mockContext
        })
      ).rejects.toThrow('Rendering failed')
    })
  })

  describe('multi-tenant isolation', () => {
    it('always includes organizationId from context', async () => {
      const tool = createPdfRenderTool(mockConfig)
      const customContext = createMockExecutionContext({
        organizationId: 'different-org-999'
      })

      const input = {
        document: {
          pages: [{ sections: [{ type: 'text', content: 'Test' }] }]
        },
        storage: { bucket: 'test', path: 'test.pdf' }
      }

      await tool.execute({
        input,
        executionContext: customContext
      })

      expect(mockPdfService.render).toHaveBeenCalledWith(
        expect.objectContaining({
          organizationId: 'different-org-999'
        })
      )
    })
  })

  describe('concurrent execution', () => {
    it('handles multiple concurrent PDF renders', async () => {
      mockPdfService.render
        .mockResolvedValueOnce({ success: true, pdfUrl: 'url1', size: 1000 })
        .mockResolvedValueOnce({ success: true, pdfUrl: 'url2', size: 2000 })

      const tool = createPdfRenderTool(mockConfig)

      const results = await Promise.all([
        tool.execute({
          input: {
            document: { pages: [{ sections: [{ type: 'text', content: 'Doc 1' }] }] },
            storage: { bucket: 'test', path: 'doc1.pdf' }
          },
          executionContext: mockContext
        }),
        tool.execute({
          input: {
            document: { pages: [{ sections: [{ type: 'text', content: 'Doc 2' }] }] },
            storage: { bucket: 'test', path: 'doc2.pdf' }
          },
          executionContext: mockContext
        })
      ])

      expect(results).toEqual([
        { success: true, pdfUrl: 'url1', size: 1000 },
        { success: true, pdfUrl: 'url2', size: 2000 }
      ])
      expect(mockPdfService.render).toHaveBeenCalledTimes(2)
    })
  })
})

describe('PdfRenderInputSchema', () => {
  describe('document validation', () => {
    it('accepts valid document with single page', () => {
      const input = {
        document: {
          pages: [{
            sections: [{ type: 'text', content: 'Hello World' }]
          }]
        },
        storage: { bucket: 'test', path: 'test.pdf' }
      }

      const result = PdfRenderInputSchema.safeParse(input)
      expect(result.success).toBe(true)
    })

    it('accepts document with multiple pages', () => {
      const input = {
        document: {
          pages: [
            { sections: [{ type: 'text', content: 'Page 1' }] },
            { sections: [{ type: 'text', content: 'Page 2' }] }
          ]
        },
        storage: { bucket: 'test', path: 'test.pdf' }
      }

      const result = PdfRenderInputSchema.safeParse(input)
      expect(result.success).toBe(true)
    })

    it('accepts document with all content block types', () => {
      const input = {
        document: {
          pages: [{
            sections: [
              { type: 'text', content: 'Title', variant: 'title' },
              { type: 'metric', value: '100', label: 'Count' },
              { type: 'list', items: ['a', 'b'], ordered: false },
              { type: 'card', title: 'Card', content: [{ type: 'text', content: 'Nested' }] }
            ]
          }]
        },
        storage: { bucket: 'test', path: 'test.pdf' }
      }

      const result = PdfRenderInputSchema.safeParse(input)
      expect(result.success).toBe(true)
    })

    it('accepts document with metadata', () => {
      const input = {
        document: {
          pages: [{ sections: [{ type: 'text', content: 'Test' }] }],
          metadata: { title: 'My Doc', author: 'Author' }
        },
        storage: { bucket: 'test', path: 'test.pdf' }
      }

      const result = PdfRenderInputSchema.safeParse(input)
      expect(result.success).toBe(true)
    })

    it('rejects document without pages', () => {
      const input = {
        document: {},
        storage: { bucket: 'test', path: 'test.pdf' }
      }

      const result = PdfRenderInputSchema.safeParse(input)
      expect(result.success).toBe(false)
    })
  })

  describe('storage validation', () => {
    it('requires storage bucket', () => {
      const input = {
        document: { pages: [{ sections: [{ type: 'text', content: 'Test' }] }] },
        storage: { path: 'test.pdf' }
      }

      const result = PdfRenderInputSchema.safeParse(input)
      expect(result.success).toBe(false)
    })

    it('requires storage path', () => {
      const input = {
        document: { pages: [{ sections: [{ type: 'text', content: 'Test' }] }] },
        storage: { bucket: 'test' }
      }

      const result = PdfRenderInputSchema.safeParse(input)
      expect(result.success).toBe(false)
    })
  })

  describe('theme validation', () => {
    it('accepts optional theme override', () => {
      const input = {
        document: { pages: [{ sections: [{ type: 'text', content: 'Test' }] }] },
        storage: { bucket: 'test', path: 'test.pdf' },
        theme: {
          colors: { primary: '#000' }
        }
      }

      const result = PdfRenderInputSchema.safeParse(input)
      expect(result.success).toBe(true)
    })

    it('works without theme (optional)', () => {
      const input = {
        document: { pages: [{ sections: [{ type: 'text', content: 'Test' }] }] },
        storage: { bucket: 'test', path: 'test.pdf' }
      }

      const result = PdfRenderInputSchema.safeParse(input)
      expect(result.success).toBe(true)
    })
  })
})
