import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'
import { renderHook, waitFor, act } from '@testing-library/react'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import React from 'react'
import { useImport } from '../useImport'
import * as api from '../../api/import'

// Mock the API
vi.mock('../../api/import', () => ({
  uploadCaddyfile: vi.fn(),
  getImportPreview: vi.fn(),
  commitImport: vi.fn(),
  cancelImport: vi.fn(),
  getImportStatus: vi.fn(),
}))

const createWrapper = () => {
  const queryClient = new QueryClient({
    defaultOptions: {
      queries: {
        retry: false,
      },
    },
  })
  return ({ children }: { children: React.ReactNode }) => (
    <QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
  )
}

describe('useImport', () => {
  beforeEach(() => {
    vi.clearAllMocks()
    vi.mocked(api.getImportStatus).mockResolvedValue({ has_pending: false })
  })

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

  it('starts with no active session', async () => {
    const { result } = renderHook(() => useImport(), { wrapper: createWrapper() })

    await waitFor(() => {
      expect(result.current.loading).toBe(false)
      expect(result.current.session).toBeNull()
    })
    expect(result.current.error).toBeNull()
  })

  it('uploads content and creates session', async () => {
    const mockSession = {
      id: 'session-1',
      state: 'reviewing' as const,
      created_at: '2025-01-18T10:00:00Z',
      updated_at: '2025-01-18T10:00:00Z',
    }

    const mockPreviewData = {
      hosts: [{ domain_names: 'test.com' }],
      conflicts: [],
      errors: [],
    }

    const mockResponse = {
      session: mockSession,
      preview: mockPreviewData,
    }

    vi.mocked(api.uploadCaddyfile).mockResolvedValue(mockResponse)
    vi.mocked(api.getImportStatus).mockResolvedValue({ has_pending: true, session: mockSession })
    vi.mocked(api.getImportPreview).mockResolvedValue(mockResponse)

    const { result } = renderHook(() => useImport(), { wrapper: createWrapper() })

    await act(async () => {
      await result.current.upload('example.com { reverse_proxy localhost:8080 }')
    })

    await waitFor(() => {
      expect(result.current.session).toEqual(mockSession)
    })

    expect(api.uploadCaddyfile).toHaveBeenCalledWith('example.com { reverse_proxy localhost:8080 }')
    expect(result.current.loading).toBe(false)
  })

  it('handles upload errors', async () => {
    const mockError = new Error('Upload failed')
    vi.mocked(api.uploadCaddyfile).mockRejectedValue(mockError)

    const { result } = renderHook(() => useImport(), { wrapper: createWrapper() })

    let threw = false
    await act(async () => {
      try {
        await result.current.upload('invalid')
      } catch {
        threw = true
      }
    })
    expect(threw).toBe(true)

    await waitFor(() => {
      expect(result.current.error).toBe('Upload failed')
    })
  })

  it('commits import with resolutions', async () => {
    const mockSession = {
      id: 'session-2',
      state: 'reviewing' as const,
      created_at: '2025-01-18T10:00:00Z',
      updated_at: '2025-01-18T10:00:00Z',
    }

    const mockResponse = {
      session: mockSession,
      preview: { hosts: [], conflicts: [], errors: [] },
    }

    let isCommitted = false
    vi.mocked(api.uploadCaddyfile).mockResolvedValue(mockResponse)
    vi.mocked(api.getImportStatus).mockImplementation(async () => {
      if (isCommitted) return { has_pending: false }
      return { has_pending: true, session: mockSession }
    })
    vi.mocked(api.getImportPreview).mockResolvedValue(mockResponse)
    vi.mocked(api.commitImport).mockImplementation(async () => {
      isCommitted = true
      return { created: 0, updated: 0, skipped: 0, errors: [] }
    })

    const { result } = renderHook(() => useImport(), { wrapper: createWrapper() })

    await act(async () => {
      await result.current.upload('test')
    })

    await waitFor(() => {
      expect(result.current.session).toEqual(mockSession)
    })

    await act(async () => {
      await result.current.commit({ 'test.com': 'skip' }, { 'test.com': 'Test' })
    })

    expect(api.commitImport).toHaveBeenCalledWith('session-2', { 'test.com': 'skip' }, { 'test.com': 'Test' })

    await waitFor(() => {
      expect(result.current.session).toBeNull()
    })
  })

  it('cancels active import session', async () => {
    const mockSession = {
      id: 'session-3',
      state: 'reviewing' as const,
      created_at: '2025-01-18T10:00:00Z',
      updated_at: '2025-01-18T10:00:00Z',
    }

    const mockResponse = {
      session: mockSession,
      preview: { hosts: [], conflicts: [], errors: [] },
    }

    let isCancelled = false
    vi.mocked(api.uploadCaddyfile).mockResolvedValue(mockResponse)
    vi.mocked(api.getImportStatus).mockImplementation(async () => {
      if (isCancelled) return { has_pending: false }
      return { has_pending: true, session: mockSession }
    })
    vi.mocked(api.getImportPreview).mockResolvedValue(mockResponse)
    vi.mocked(api.cancelImport).mockImplementation(async () => {
      isCancelled = true
    })

    const { result } = renderHook(() => useImport(), { wrapper: createWrapper() })

    await act(async () => {
      await result.current.upload('test')
    })

    await waitFor(() => {
      expect(result.current.session).toEqual(mockSession)
    })

    await act(async () => {
      await result.current.cancel()
    })

    expect(api.cancelImport).toHaveBeenCalled()
    await waitFor(() => {
      expect(result.current.session).toBeNull()
    })
  })

  it('handles commit errors', async () => {
    const mockSession = {
      id: 'session-4',
      state: 'reviewing' as const,
      created_at: '2025-01-18T10:00:00Z',
      updated_at: '2025-01-18T10:00:00Z',
    }

    const mockResponse = {
      session: mockSession,
      preview: { hosts: [], conflicts: [], errors: [] },
    }

    vi.mocked(api.uploadCaddyfile).mockResolvedValue(mockResponse)
    vi.mocked(api.getImportStatus).mockResolvedValue({ has_pending: true, session: mockSession })
    vi.mocked(api.getImportPreview).mockResolvedValue(mockResponse)

    const mockError = new Error('Commit failed')
    vi.mocked(api.commitImport).mockRejectedValue(mockError)

    const { result } = renderHook(() => useImport(), { wrapper: createWrapper() })

    await act(async () => {
      await result.current.upload('test')
    })

    await waitFor(() => {
      expect(result.current.session).toEqual(mockSession)
    })

    let threw = false
    await act(async () => {
      try {
        await result.current.commit({}, {})
      } catch {
        threw = true
      }
    })
    expect(threw).toBe(true)

    await waitFor(() => {
      expect(result.current.error).toBe('Commit failed')
    })
  })

  it('captures and exposes commit result on success', async () => {
    const mockSession = {
      id: 'session-5',
      state: 'reviewing' as const,
      created_at: '2025-01-18T10:00:00Z',
      updated_at: '2025-01-18T10:00:00Z',
    }

    const mockResponse = {
      session: mockSession,
      preview: { hosts: [], conflicts: [], errors: [] },
    }

    const mockCommitResult = {
      created: 3,
      updated: 1,
      skipped: 2,
      errors: [],
    }

    let isCommitted = false
    vi.mocked(api.uploadCaddyfile).mockResolvedValue(mockResponse)
    vi.mocked(api.getImportStatus).mockImplementation(async () => {
      if (isCommitted) return { has_pending: false }
      return { has_pending: true, session: mockSession }
    })
    vi.mocked(api.getImportPreview).mockResolvedValue(mockResponse)
    vi.mocked(api.commitImport).mockImplementation(async () => {
      isCommitted = true
      return mockCommitResult
    })

    const { result } = renderHook(() => useImport(), { wrapper: createWrapper() })

    await act(async () => {
      await result.current.upload('test')
    })

    await waitFor(() => {
      expect(result.current.session).toEqual(mockSession)
    })

    await act(async () => {
      await result.current.commit({}, {})
    })

    expect(result.current.commitResult).toEqual(mockCommitResult)
    expect(result.current.commitSuccess).toBe(true)
  })

  it('clears commit result when clearCommitResult is called', async () => {
    const mockSession = {
      id: 'session-6',
      state: 'reviewing' as const,
      created_at: '2025-01-18T10:00:00Z',
      updated_at: '2025-01-18T10:00:00Z',
    }

    const mockResponse = {
      session: mockSession,
      preview: { hosts: [], conflicts: [], errors: [] },
    }

    const mockCommitResult = {
      created: 2,
      updated: 0,
      skipped: 0,
      errors: [],
    }

    let isCommitted = false
    vi.mocked(api.uploadCaddyfile).mockResolvedValue(mockResponse)
    vi.mocked(api.getImportStatus).mockImplementation(async () => {
      if (isCommitted) return { has_pending: false }
      return { has_pending: true, session: mockSession }
    })
    vi.mocked(api.getImportPreview).mockResolvedValue(mockResponse)
    vi.mocked(api.commitImport).mockImplementation(async () => {
      isCommitted = true
      return mockCommitResult
    })

    const { result } = renderHook(() => useImport(), { wrapper: createWrapper() })

    await act(async () => {
      await result.current.upload('test')
    })

    await waitFor(() => {
      expect(result.current.session).toEqual(mockSession)
    })

    await act(async () => {
      await result.current.commit({}, {})
    })

    expect(result.current.commitResult).toEqual(mockCommitResult)

    act(() => {
      result.current.clearCommitResult()
    })

    expect(result.current.commitResult).toBeNull()
    expect(result.current.commitSuccess).toBe(false)
  })
})
