import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'
import { listCampaigns } from '../index.js'
import type { ExecutionContext } from '../../../../../../../../base/types.js'

// Mock fetch globally
const mockFetch = vi.fn()
vi.stubGlobal('fetch', mockFetch)

const createMockContext = (): ExecutionContext => ({
  executionId: 'exec-456',
  organizationId: 'org-123',
  organizationName: 'Test Org',
  userId: 'user-789',
  resourceId: 'test-resource',
  executionDepth: 0,
  logger: { info: vi.fn(), warn: vi.fn(), error: vi.fn(), debug: vi.fn() },
  store: new Map()
})

describe('listCampaigns', () => {
  beforeEach(() => {
    vi.clearAllMocks()
  })

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

  const validCredentials = { apiKey: 'test-api-key' }

  describe('Success Cases', () => {
    it('should return campaigns from array response', async () => {
      const campaignData = [{ id: 'c1', name: 'Campaign 1', status: 'active', created_at: '2026-01-01' }]
      mockFetch.mockResolvedValueOnce({
        ok: true,
        json: () => Promise.resolve(campaignData)
      })

      const context = createMockContext()
      const result = await listCampaigns(validCredentials, {}, context)

      expect(result).toEqual({
        campaigns: [{ id: 'c1', name: 'Campaign 1', status: 'active', created_at: '2026-01-01' }]
      })
    })

    it('should handle wrapped { data: [...] } response', async () => {
      const campaignData = [{ id: 'c1', name: 'Campaign 1', status: 'active', created_at: '2026-01-01' }]
      mockFetch.mockResolvedValueOnce({
        ok: true,
        json: () => Promise.resolve({ data: campaignData })
      })

      const result = await listCampaigns(validCredentials, {})

      expect(result).toEqual({
        campaigns: [{ id: 'c1', name: 'Campaign 1', status: 'active', created_at: '2026-01-01' }]
      })
    })

    it('should handle { campaigns: [...] } response shape', async () => {
      const campaignData = [{ id: 'c1', name: 'Campaign 1', status: 'active', created_at: '2026-01-01' }]
      mockFetch.mockResolvedValueOnce({
        ok: true,
        json: () => Promise.resolve({ campaigns: campaignData })
      })

      const result = await listCampaigns(validCredentials, {})

      expect(result).toEqual({
        campaigns: [{ id: 'c1', name: 'Campaign 1', status: 'active', created_at: '2026-01-01' }]
      })
    })

    it('should handle { items: [...] } response shape', async () => {
      const campaignData = [{ id: 'c1', name: 'Campaign 1', status: 'active', created_at: '2026-01-01' }]
      mockFetch.mockResolvedValueOnce({
        ok: true,
        json: () => Promise.resolve({ items: campaignData })
      })

      const result = await listCampaigns(validCredentials, {})

      expect(result).toEqual({
        campaigns: [{ id: 'c1', name: 'Campaign 1', status: 'active', created_at: '2026-01-01' }]
      })
    })

    it('should return next_starting_after when present in response', async () => {
      const campaignData = [{ id: 'c1', name: 'Campaign 1', status: 'active', created_at: '2026-01-01' }]
      mockFetch.mockResolvedValueOnce({
        ok: true,
        json: () => Promise.resolve({ campaigns: campaignData, next_starting_after: 'cursor-abc' })
      })

      const result = await listCampaigns(validCredentials, {})

      expect(result).toEqual({
        campaigns: [{ id: 'c1', name: 'Campaign 1', status: 'active', created_at: '2026-01-01' }],
        next_starting_after: 'cursor-abc'
      })
    })

    it('should return empty campaigns for empty response {}', async () => {
      mockFetch.mockResolvedValueOnce({
        ok: true,
        json: () => Promise.resolve({})
      })

      const result = await listCampaigns(validCredentials, {})

      expect(result).toEqual({ campaigns: [] })
    })
  })

  describe('URL Construction', () => {
    it('should call exact base URL with no query string when no params provided', async () => {
      mockFetch.mockResolvedValueOnce({
        ok: true,
        json: () => Promise.resolve([])
      })

      await listCampaigns(validCredentials, {})

      expect(mockFetch).toHaveBeenCalledWith(
        'https://api.instantly.ai/api/v2/campaigns',
        expect.objectContaining({
          headers: {
            Authorization: 'Bearer test-api-key'
          }
        })
      )
    })

    it('should include limit and status as query params', async () => {
      mockFetch.mockResolvedValueOnce({
        ok: true,
        json: () => Promise.resolve([])
      })

      await listCampaigns(validCredentials, { limit: 10, status: 'active' })

      expect(mockFetch).toHaveBeenCalledWith(
        'https://api.instantly.ai/api/v2/campaigns?limit=10&status=active',
        expect.objectContaining({
          headers: {
            Authorization: 'Bearer test-api-key'
          }
        })
      )
    })

    it('should include search as query param', async () => {
      mockFetch.mockResolvedValueOnce({
        ok: true,
        json: () => Promise.resolve([])
      })

      await listCampaigns(validCredentials, { search: 'vet' })

      expect(mockFetch).toHaveBeenCalledWith(
        'https://api.instantly.ai/api/v2/campaigns?search=vet',
        expect.objectContaining({
          headers: {
            Authorization: 'Bearer test-api-key'
          }
        })
      )
    })

    it('should include starting_after as query param', async () => {
      mockFetch.mockResolvedValueOnce({
        ok: true,
        json: () => Promise.resolve([])
      })

      await listCampaigns(validCredentials, { starting_after: 'cursor123' })

      expect(mockFetch).toHaveBeenCalledWith(
        'https://api.instantly.ai/api/v2/campaigns?starting_after=cursor123',
        expect.objectContaining({
          headers: {
            Authorization: 'Bearer test-api-key'
          }
        })
      )
    })
  })
})
