import { describe, it, expect, vi, beforeEach } from 'vitest'
import { createPlatformEmailTool, SendPlatformEmailInputSchema } from '../email'
import { ToolingError } from '../../types'
import { getToolServices } from '../../registry'
import { createMockExecutionContext } from '../../../test-utils/mocks'

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

describe('createPlatformEmailTool', () => {
  const mockConfig = {
    name: 'send_email',
    description: 'Send platform notification emails'
  }

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

  const mockEmailService = {
    send: vi.fn().mockResolvedValue({ sent: 2, failed: 0 })
  }

  beforeEach(() => {
    vi.clearAllMocks()
    mockEmailService.send.mockResolvedValue({ sent: 2, failed: 0 })
    vi.mocked(getToolServices).mockReturnValue({
      commandQueueService: null,
      taskSchedulerService: null,
      notificationsService: null,
      emailService: mockEmailService,
      credentialsService: null,
      integrationService: null,
      leadService: null
    })
  })

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

      expect(tool.name).toBe('send_email')
      expect(tool.description).toBe('Send platform notification emails')
    })

    it('uses SendPlatformEmailInputSchema as inputSchema', () => {
      const tool = createPlatformEmailTool(mockConfig)

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

  describe('execute with explicit userIds', () => {
    it('sends email to specified users', async () => {
      const tool = createPlatformEmailTool(mockConfig)
      const input = {
        userIds: ['user-1', 'user-2'],
        subject: 'Test Subject',
        html: '<p>Hello</p>'
      }

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

      expect(result).toEqual({ sent: 2, failed: 0 })
      expect(mockEmailService.send).toHaveBeenCalledWith({
        organizationId: 'org-123',
        userIds: ['user-1', 'user-2'],
        targetRole: undefined,
        targetAll: undefined,
        subject: 'Test Subject',
        html: '<p>Hello</p>',
        text: undefined,
        replyTo: undefined,
        tags: undefined
      })
    })

    it('passes optional parameters correctly', async () => {
      const tool = createPlatformEmailTool(mockConfig)
      const input = {
        userIds: ['user-1'],
        subject: 'Test',
        text: 'Plain text email',
        replyTo: 'support@example.com',
        tags: [{ name: 'category', value: 'alert' }]
      }

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

      expect(mockEmailService.send).toHaveBeenCalledWith(
        expect.objectContaining({
          text: 'Plain text email',
          replyTo: 'support@example.com',
          tags: [{ name: 'category', value: 'alert' }]
        })
      )
    })
  })

  describe('execute with role-based targeting', () => {
    it('sends email to users with specified role', async () => {
      const tool = createPlatformEmailTool(mockConfig)
      const input = {
        targetRole: 'admin',
        subject: 'Admin Notice',
        html: '<p>Admin content</p>'
      }

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

      expect(mockEmailService.send).toHaveBeenCalledWith(
        expect.objectContaining({
          organizationId: 'org-123',
          targetRole: 'admin',
          userIds: undefined,
          targetAll: undefined
        })
      )
    })
  })

  describe('execute with broadcast targeting', () => {
    it('sends email to all org members', async () => {
      const tool = createPlatformEmailTool(mockConfig)
      const input = {
        targetAll: true,
        subject: 'Company Announcement',
        text: 'Important update for everyone'
      }

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

      expect(mockEmailService.send).toHaveBeenCalledWith(
        expect.objectContaining({
          organizationId: 'org-123',
          targetAll: true,
          userIds: undefined,
          targetRole: undefined
        })
      )
    })
  })

  describe('partial success handling', () => {
    it('returns result with failed count and errors', async () => {
      mockEmailService.send.mockResolvedValue({
        sent: 1,
        failed: 1,
        errors: [
          { userId: 'user-2', email: 'bad@example.com', error: 'Invalid email' }
        ]
      })

      const tool = createPlatformEmailTool(mockConfig)
      const input = {
        userIds: ['user-1', 'user-2'],
        subject: 'Test',
        html: '<p>Test</p>'
      }

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

      expect(result).toEqual({
        sent: 1,
        failed: 1,
        errors: [
          { userId: 'user-2', email: 'bad@example.com', error: 'Invalid email' }
        ]
      })
    })
  })

  describe('error handling', () => {
    it('throws ToolingError when executionContext is missing', async () => {
      const tool = createPlatformEmailTool(mockConfig)
      const input = {
        userIds: ['user-1'],
        subject: 'Test',
        html: '<p>Test</p>'
      }

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

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

      const tool = createPlatformEmailTool(mockConfig)
      const input = {
        userIds: ['user-1'],
        subject: 'Test',
        html: '<p>Test</p>'
      }

      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('EmailService is unavailable')
      }
    })

    it('propagates errors from emailService', async () => {
      mockEmailService.send.mockRejectedValue(new Error('All recipients invalid'))

      const tool = createPlatformEmailTool(mockConfig)
      const input = {
        userIds: ['user-1'],
        subject: 'Test',
        html: '<p>Test</p>'
      }

      await expect(
        tool.execute({
          input,
          executionContext: mockContext
        })
      ).rejects.toThrow('All recipients invalid')
    })
  })

  describe('concurrent execution', () => {
    it('handles multiple concurrent email sends', async () => {
      mockEmailService.send
        .mockResolvedValueOnce({ sent: 1, failed: 0 })
        .mockResolvedValueOnce({ sent: 3, failed: 0 })

      const tool = createPlatformEmailTool(mockConfig)

      const results = await Promise.all([
        tool.execute({
          input: { userIds: ['u1'], subject: 'S1', html: '<p>1</p>' },
          executionContext: mockContext
        }),
        tool.execute({
          input: { targetAll: true, subject: 'S2', text: 'Broadcast' },
          executionContext: mockContext
        })
      ])

      expect(results).toEqual([
        { sent: 1, failed: 0 },
        { sent: 3, failed: 0 }
      ])
      expect(mockEmailService.send).toHaveBeenCalledTimes(2)
    })
  })
})

describe('SendPlatformEmailInputSchema', () => {
  describe('targeting validation', () => {
    it('accepts valid userIds targeting', () => {
      const input = {
        userIds: ['550e8400-e29b-41d4-a716-446655440000'],
        subject: 'Test',
        html: '<p>Test</p>'
      }

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

    it('accepts valid role-based targeting', () => {
      const input = {
        targetRole: 'admin',
        subject: 'Test',
        text: 'Test'
      }

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

    it('accepts valid broadcast targeting', () => {
      const input = {
        targetAll: true,
        subject: 'Test',
        html: '<p>Test</p>'
      }

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

    it('rejects when no targeting mode specified', () => {
      const input = {
        subject: 'Test',
        html: '<p>Test</p>'
      }

      const result = SendPlatformEmailInputSchema.safeParse(input)
      expect(result.success).toBe(false)
      if (!result.success) {
        expect(result.error.issues[0].message).toContain('Exactly one targeting mode required')
      }
    })

    it('rejects when multiple targeting modes specified', () => {
      const input = {
        userIds: ['550e8400-e29b-41d4-a716-446655440000'],
        targetRole: 'admin',
        subject: 'Test',
        html: '<p>Test</p>'
      }

      const result = SendPlatformEmailInputSchema.safeParse(input)
      expect(result.success).toBe(false)
      if (!result.success) {
        expect(result.error.issues[0].message).toContain('Exactly one targeting mode required')
      }
    })

    it('rejects empty userIds array', () => {
      const input = {
        userIds: [],
        subject: 'Test',
        html: '<p>Test</p>'
      }

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

    it('validates userIds are UUIDs', () => {
      const input = {
        userIds: ['not-a-uuid'],
        subject: 'Test',
        html: '<p>Test</p>'
      }

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

  describe('content validation', () => {
    it('requires either html or text', () => {
      const input = {
        userIds: ['550e8400-e29b-41d4-a716-446655440000'],
        subject: 'Test'
      }

      const result = SendPlatformEmailInputSchema.safeParse(input)
      expect(result.success).toBe(false)
      if (!result.success) {
        expect(result.error.issues[0].message).toContain('Must provide either html or text body')
      }
    })

    it('accepts html only', () => {
      const input = {
        userIds: ['550e8400-e29b-41d4-a716-446655440000'],
        subject: 'Test',
        html: '<p>HTML content</p>'
      }

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

    it('accepts text only', () => {
      const input = {
        userIds: ['550e8400-e29b-41d4-a716-446655440000'],
        subject: 'Test',
        text: 'Plain text content'
      }

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

    it('accepts both html and text', () => {
      const input = {
        userIds: ['550e8400-e29b-41d4-a716-446655440000'],
        subject: 'Test',
        html: '<p>HTML</p>',
        text: 'Plain text'
      }

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

    it('validates subject length', () => {
      const input = {
        userIds: ['550e8400-e29b-41d4-a716-446655440000'],
        subject: '',
        html: '<p>Test</p>'
      }

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

    it('rejects subject over 200 characters', () => {
      const input = {
        userIds: ['550e8400-e29b-41d4-a716-446655440000'],
        subject: 'a'.repeat(201),
        html: '<p>Test</p>'
      }

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

  describe('optional fields validation', () => {
    it('validates replyTo is valid email', () => {
      const input = {
        userIds: ['550e8400-e29b-41d4-a716-446655440000'],
        subject: 'Test',
        html: '<p>Test</p>',
        replyTo: 'not-an-email'
      }

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

    it('accepts valid replyTo email', () => {
      const input = {
        userIds: ['550e8400-e29b-41d4-a716-446655440000'],
        subject: 'Test',
        html: '<p>Test</p>',
        replyTo: 'support@example.com'
      }

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

    it('accepts valid tags array', () => {
      const input = {
        userIds: ['550e8400-e29b-41d4-a716-446655440000'],
        subject: 'Test',
        html: '<p>Test</p>',
        tags: [
          { name: 'category', value: 'alert' },
          { name: 'priority', value: 'high' }
        ]
      }

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