import {renderHook, act} from '@testing-library/react'
import {describe, expect, it, vi, beforeEach} from 'vitest'

import {useShopActions} from '../../internal/useShopActions'

import {useImageUpload} from './useImageUpload'

// Mock dependencies
vi.mock('../../internal/useShopActions', () => ({
  useShopActions: vi.fn(() => ({
    createImageUploadLink: vi.fn(),
    completeImageUpload: vi.fn(),
  })),
}))

vi.mock('../../utils', () => ({
  fileToDataUri: vi.fn((file: File) =>
    Promise.resolve(`data:${file.type};base64,mockbase64data`)
  ),
}))

// Mock fetch globally
global.fetch = vi.fn()

describe('useImageUpload', () => {
  let mockCreateImageUploadLink: ReturnType<typeof vi.fn>
  let mockCompleteImageUpload: ReturnType<typeof vi.fn>

  beforeEach(() => {
    vi.clearAllMocks()

    // Reset fetch mock with proper blob() implementation
    ;(global.fetch as any).mockImplementation(async (url: string) => {
      // For data URI fetch (fileToDataUri result)
      if (url.startsWith('data:')) {
        return {
          blob: async () => new Blob(['test image data'], {type: 'image/jpeg'}),
        }
      }
      // Default for other fetches
      return {
        ok: true,
        text: async () => 'Upload successful',
      }
    })

    // Set up mock actions with proper implementations
    mockCreateImageUploadLink = vi.fn().mockResolvedValue({
      ok: true,
      data: {
        targets: [
          {
            url: 'https://storage.googleapis.com/upload',
            resourceUrl: 'https://storage.googleapis.com/resource/123',
            parameters: [
              {name: 'key', value: 'test-key'},
              {name: 'policy', value: 'test-policy'},
            ],
          },
        ],
      },
    })

    mockCompleteImageUpload = vi.fn().mockResolvedValue({
      ok: true,
      data: {
        files: [
          {
            id: 'uploaded-image-id',
            fileStatus: 'READY',
            image: {
              url: 'https://example.com/image.jpg',
            },
          },
        ],
      },
    })

    // Update the mocks to return our mock actions
    ;(useShopActions as any).mockReturnValue({
      createImageUploadLink: mockCreateImageUploadLink,
      completeImageUpload: mockCompleteImageUpload,
    })
  })

  describe('uploadImage', () => {
    it('successfully uploads an image', async () => {
      const {result} = renderHook(() => useImageUpload())

      const testFile = new File(['test image'], 'test.jpg', {
        type: 'image/jpeg',
      })

      let uploadedImages: any
      await act(async () => {
        uploadedImages = await result.current.uploadImage(testFile)
      })

      // Verify the upload flow
      expect(mockCreateImageUploadLink).toHaveBeenCalledWith({
        input: [
          {
            mimeType: 'image/jpeg',
            fileSize: 10, // 'test image'.length
          },
        ],
      })

      // Verify GCS upload
      expect(global.fetch).toHaveBeenCalledWith(
        'https://storage.googleapis.com/upload',
        expect.objectContaining({
          method: 'POST',
          body: expect.any(FormData),
        })
      )

      expect(mockCompleteImageUpload).toHaveBeenCalledWith({
        resourceUrls: ['https://storage.googleapis.com/resource/123'],
      })

      expect(uploadedImages).toEqual([
        {
          id: 'uploaded-image-id',
          imageUrl: 'https://example.com/image.jpg',
          resourceUrl: 'https://storage.googleapis.com/resource/123',
        },
      ])
    })

    it('throws error when createImageUploadLink fails', async () => {
      mockCreateImageUploadLink.mockResolvedValue({
        ok: false,
        error: {
          message: 'Failed to create upload link',
        },
      })

      const {result} = renderHook(() => useImageUpload())

      const testFile = new File(['test'], 'test.jpg', {type: 'image/jpeg'})

      await expect(
        act(async () => {
          await result.current.uploadImage(testFile)
        })
      ).rejects.toThrow('Failed to create upload link')
    })

    it('throws error when GCS upload fails', async () => {
      // Mock failed fetch for GCS upload
      ;(global.fetch as any).mockImplementation(async (url: string) => {
        if (url.startsWith('data:')) {
          return {
            blob: async () => new Blob(['test'], {type: 'image/jpeg'}),
          }
        }
        // GCS upload fails
        return {
          ok: false,
          text: async () => 'Upload failed',
        }
      })

      const {result} = renderHook(() => useImageUpload())

      const testFile = new File(['test'], 'test.jpg', {type: 'image/jpeg'})

      await expect(
        act(async () => {
          await result.current.uploadImage(testFile)
        })
      ).rejects.toThrow('Failed to upload image')
    })

    it('throws error when completeImageUpload fails', async () => {
      mockCompleteImageUpload.mockResolvedValue({
        ok: false,
        error: {
          message: 'Failed to complete upload',
        },
      })

      const {result} = renderHook(() => useImageUpload())

      const testFile = new File(['test'], 'test.jpg', {type: 'image/jpeg'})

      await expect(
        act(async () => {
          await result.current.uploadImage(testFile)
        })
      ).rejects.toThrow('Failed to complete upload')
    })

    it('polls until image is ready', async () => {
      // First two calls return PROCESSING, third returns READY
      mockCompleteImageUpload
        .mockResolvedValueOnce({
          ok: true,
          data: {
            files: [
              {
                id: 'uploaded-image-id',
                fileStatus: 'PROCESSING',
              },
            ],
          },
        })
        .mockResolvedValueOnce({
          ok: true,
          data: {
            files: [
              {
                id: 'uploaded-image-id',
                fileStatus: 'PROCESSING',
              },
            ],
          },
        })
        .mockResolvedValueOnce({
          ok: true,
          data: {
            files: [
              {
                id: 'uploaded-image-id',
                fileStatus: 'READY',
                image: {
                  url: 'https://example.com/processed.jpg',
                },
              },
            ],
          },
        })

      // Speed up test by mocking setTimeout
      vi.useFakeTimers()

      const {result} = renderHook(() => useImageUpload())

      const testFile = new File(['test'], 'test.jpg', {type: 'image/jpeg'})

      // Start the upload without awaiting
      let uploadPromise: Promise<any>
      await act(async () => {
        uploadPromise = result.current.uploadImage(testFile)
      })

      // Advance timers for polling
      await act(async () => {
        await vi.advanceTimersByTimeAsync(2000)
      })

      const uploadedImages = await uploadPromise!

      expect(mockCompleteImageUpload).toHaveBeenCalledTimes(3)
      expect(uploadedImages).toEqual([
        {
          id: 'uploaded-image-id',
          imageUrl: 'https://example.com/processed.jpg',
          resourceUrl: 'https://storage.googleapis.com/resource/123',
        },
      ])

      vi.useRealTimers()
    })

    it('handles file without initial size', async () => {
      const {result} = renderHook(() => useImageUpload())

      // Create a file without size property set
      const testFile = new File(['test'], 'test.jpg', {type: 'image/jpeg'})

      let uploadedImages: any
      await act(async () => {
        uploadedImages = await result.current.uploadImage(testFile)
      })

      expect(uploadedImages).toEqual([
        {
          id: 'uploaded-image-id',
          imageUrl: 'https://example.com/image.jpg',
          resourceUrl: 'https://storage.googleapis.com/resource/123',
        },
      ])
    })

    it('includes all form data parameters in GCS upload', async () => {
      let capturedFormData: FormData | undefined
      ;(global.fetch as any).mockImplementation(
        async (url: string, options: any) => {
          if (url.startsWith('data:')) {
            return {
              blob: async () => new Blob(['test'], {type: 'image/jpeg'}),
            }
          }
          // eslint-disable-next-line jest/no-if
          if (url === 'https://storage.googleapis.com/upload') {
            capturedFormData = options.body
          }
          return {
            ok: true,
            text: async () => 'Upload successful',
          }
        }
      )

      const {result} = renderHook(() => useImageUpload())

      const testFile = new File(['test'], 'test.jpg', {type: 'image/jpeg'})

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

      // Verify FormData contains all expected fields
      expect(capturedFormData).toBeDefined()
      expect(capturedFormData?.get('key')).toBe('test-key')
      expect(capturedFormData?.get('policy')).toBe('test-policy')
      expect(capturedFormData?.get('file')).toBeDefined()
    })
  })
})
