import { describe, it, expect, vi, beforeEach } from 'vitest'
import {
  getChallenge,
  createChallenge,
  verifyChallenge,
  pollChallenge,
  deleteChallenge,
} from '../manualChallenge'
import client from '../client'

vi.mock('../client', () => ({
  default: {
    get: vi.fn(),
    post: vi.fn(),
    delete: vi.fn(),
  },
}))

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

  describe('getChallenge', () => {
    it('fetches challenge by provider and challenge ID', async () => {
      const mockChallenge = {
        id: 'challenge-uuid',
        status: 'pending',
        fqdn: '_acme-challenge.example.com',
        value: 'test-value',
        ttl: 300,
        created_at: '2026-01-11T00:00:00Z',
        expires_at: '2026-01-11T00:10:00Z',
        dns_propagated: false,
      }

      vi.mocked(client.get).mockResolvedValueOnce({ data: mockChallenge })

      const result = await getChallenge(1, 'challenge-uuid')

      expect(client.get).toHaveBeenCalledWith(
        '/dns-providers/1/manual-challenge/challenge-uuid'
      )
      expect(result).toEqual(mockChallenge)
    })

    it('throws error when challenge not found', async () => {
      vi.mocked(client.get).mockRejectedValueOnce({
        response: { status: 404, data: { error: 'Challenge not found' } },
      })

      await expect(getChallenge(1, 'invalid-uuid')).rejects.toMatchObject({
        response: { status: 404 },
      })
    })
  })

  describe('createChallenge', () => {
    it('creates a new challenge for the provider', async () => {
      const mockChallenge = {
        id: 'new-challenge-uuid',
        status: 'created',
        fqdn: '_acme-challenge.example.com',
        value: 'generated-value',
        ttl: 300,
        created_at: '2026-01-11T00:00:00Z',
        expires_at: '2026-01-11T00:10:00Z',
        dns_propagated: false,
      }

      vi.mocked(client.post).mockResolvedValueOnce({ data: mockChallenge })

      const result = await createChallenge(1, { domain: 'example.com' })

      expect(client.post).toHaveBeenCalledWith('/dns-providers/1/manual-challenge', {
        domain: 'example.com',
      })
      expect(result).toEqual(mockChallenge)
    })

    it('throws error when provider not found', async () => {
      vi.mocked(client.post).mockRejectedValueOnce({
        response: { status: 404, data: { error: 'Provider not found' } },
      })

      await expect(createChallenge(999, { domain: 'example.com' })).rejects.toMatchObject({
        response: { status: 404 },
      })
    })

    it('throws error when challenge already in progress', async () => {
      vi.mocked(client.post).mockRejectedValueOnce({
        response: { status: 409, data: { code: 'CHALLENGE_IN_PROGRESS' } },
      })

      await expect(createChallenge(1, { domain: 'example.com' })).rejects.toMatchObject({
        response: { status: 409 },
      })
    })
  })

  describe('verifyChallenge', () => {
    it('triggers verification for a challenge', async () => {
      const mockResult = {
        success: true,
        dns_found: true,
        message: 'TXT record verified successfully',
      }

      vi.mocked(client.post).mockResolvedValueOnce({ data: mockResult })

      const result = await verifyChallenge(1, 'challenge-uuid')

      expect(client.post).toHaveBeenCalledWith(
        '/dns-providers/1/manual-challenge/challenge-uuid/verify'
      )
      expect(result).toEqual(mockResult)
    })

    it('returns dns_found false when record not propagated', async () => {
      const mockResult = {
        success: false,
        dns_found: false,
        message: 'DNS record not found',
      }

      vi.mocked(client.post).mockResolvedValueOnce({ data: mockResult })

      const result = await verifyChallenge(1, 'challenge-uuid')

      expect(result.success).toBe(false)
      expect(result.dns_found).toBe(false)
    })

    it('throws error when challenge expired', async () => {
      vi.mocked(client.post).mockRejectedValueOnce({
        response: { status: 410, data: { code: 'CHALLENGE_EXPIRED' } },
      })

      await expect(verifyChallenge(1, 'challenge-uuid')).rejects.toMatchObject({
        response: { status: 410 },
      })
    })
  })

  describe('pollChallenge', () => {
    it('returns current challenge status', async () => {
      const mockPoll = {
        status: 'pending',
        dns_propagated: false,
        time_remaining_seconds: 480,
        last_check_at: '2026-01-11T00:02:00Z',
      }

      vi.mocked(client.get).mockResolvedValueOnce({ data: mockPoll })

      const result = await pollChallenge(1, 'challenge-uuid')

      expect(client.get).toHaveBeenCalledWith(
        '/dns-providers/1/manual-challenge/challenge-uuid/poll'
      )
      expect(result).toEqual(mockPoll)
    })

    it('returns verified status when DNS propagated', async () => {
      const mockPoll = {
        status: 'verified',
        dns_propagated: true,
        time_remaining_seconds: 0,
        last_check_at: '2026-01-11T00:05:00Z',
      }

      vi.mocked(client.get).mockResolvedValueOnce({ data: mockPoll })

      const result = await pollChallenge(1, 'challenge-uuid')

      expect(result.status).toBe('verified')
      expect(result.dns_propagated).toBe(true)
    })

    it('includes error message when challenge failed', async () => {
      const mockPoll = {
        status: 'failed',
        dns_propagated: false,
        time_remaining_seconds: 0,
        last_check_at: '2026-01-11T00:05:00Z',
        error_message: 'ACME validation failed',
      }

      vi.mocked(client.get).mockResolvedValueOnce({ data: mockPoll })

      const result = await pollChallenge(1, 'challenge-uuid')

      expect(result.status).toBe('failed')
      expect(result.error_message).toBe('ACME validation failed')
    })
  })

  describe('deleteChallenge', () => {
    it('deletes/cancels a challenge', async () => {
      vi.mocked(client.delete).mockResolvedValueOnce({ data: undefined })

      await deleteChallenge(1, 'challenge-uuid')

      expect(client.delete).toHaveBeenCalledWith(
        '/dns-providers/1/manual-challenge/challenge-uuid'
      )
    })

    it('throws error when challenge not found', async () => {
      vi.mocked(client.delete).mockRejectedValueOnce({
        response: { status: 404, data: { error: 'Challenge not found' } },
      })

      await expect(deleteChallenge(1, 'invalid-uuid')).rejects.toMatchObject({
        response: { status: 404 },
      })
    })

    it('throws error when unauthorized', async () => {
      vi.mocked(client.delete).mockRejectedValueOnce({
        response: { status: 403, data: { error: 'Unauthorized' } },
      })

      await expect(deleteChallenge(1, 'challenge-uuid')).rejects.toMatchObject({
        response: { status: 403 },
      })
    })
  })
})
