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

import {useHandleAction} from '../../internal/useHandleAction'
import {useShopActions} from '../../internal/useShopActions'
import {useImageUpload} from '../storage/useImageUpload'

import {useCreateImageContent} from './useCreateImageContent'

// Mock the internal hooks and utilities
vi.mock('../../internal/useShopActions', () => ({
  useShopActions: vi.fn(() => ({
    createContent: vi.fn(),
  })),
}))

vi.mock('../../internal/useHandleAction', () => ({
  useHandleAction: vi.fn((action: any) => action),
}))

vi.mock('../storage/useImageUpload', () => ({
  useImageUpload: vi.fn(() => ({
    uploadImage: vi.fn(),
  })),
}))

describe('useCreateImageContent', () => {
  let mockCreateContent: ReturnType<typeof vi.fn>
  let mockUploadImage: ReturnType<typeof vi.fn>

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

    // Set up mock actions with proper implementations
    mockCreateContent = vi.fn().mockResolvedValue({
      data: {
        publicId: 'content-123',
        image: {
          id: 'img-123',
          url: 'https://example.com/content-image.jpg',
          width: 800,
          height: 600,
        },
        title: 'Test Content',
        visibility: ['DISCOVERABLE'],
      },
    })

    mockUploadImage = vi.fn().mockResolvedValue([
      {
        id: 'upload-123',
        imageUrl: 'https://example.com/uploaded-image.jpg',
        resourceUrl: 'https://example.com/resource/123',
      },
    ])

    // Update the mocks to return our mock actions
    ;(useShopActions as ReturnType<typeof vi.fn>).mockReturnValue({
      createContent: mockCreateContent,
    })
    ;(useImageUpload as ReturnType<typeof vi.fn>).mockReturnValue({
      uploadImage: mockUploadImage,
    })

    // Make useHandleAction return the action directly
    ;(useHandleAction as ReturnType<typeof vi.fn>).mockImplementation(
      (action: any) => action
    )
  })

  describe('Hook Structure', () => {
    it('returns expected properties', () => {
      const {result} = renderHook(() => useCreateImageContent())

      expect(result.current).toHaveProperty('createImageContent')
      expect(result.current).toHaveProperty('loading')
      expect(typeof result.current.createImageContent).toBe('function')
      expect(typeof result.current.loading).toBe('boolean')
    })

    it('initializes with loading false', () => {
      const {result} = renderHook(() => useCreateImageContent())
      expect(result.current.loading).toBe(false)
    })
  })

  describe('createImageContent', () => {
    it('successfully creates image content', async () => {
      const {result} = renderHook(() => useCreateImageContent())

      const imageFile = new File(['test'], 'test.jpg', {type: 'image/jpeg'})
      const params = {
        image: imageFile,
        contentTitle: 'Test Content',
        visibility: ['DISCOVERABLE'] as any,
      }

      await act(async () => {
        const content = await result.current.createImageContent(params)

        expect(content.data).toEqual({
          publicId: 'content-123',
          image: {
            id: 'img-123',
            url: 'https://example.com/content-image.jpg',
            width: 800,
            height: 600,
          },
          title: 'Test Content',
          visibility: ['DISCOVERABLE'],
        })
      })

      // Verify upload was called
      expect(mockUploadImage).toHaveBeenCalledWith(imageFile)
      expect(mockUploadImage).toHaveBeenCalledTimes(1)

      // Verify createContent was called with correct params
      expect(mockCreateContent).toHaveBeenCalledWith({
        title: 'Test Content',
        imageUrl: 'https://example.com/uploaded-image.jpg',
        visibility: ['DISCOVERABLE'],
      })
      expect(mockCreateContent).toHaveBeenCalledTimes(1)
    })

    it('sets loading state during operation', async () => {
      const {result} = renderHook(() => useCreateImageContent())

      const imageFile = new File(['test'], 'test.jpg', {type: 'image/jpeg'})
      const params = {
        image: imageFile,
        contentTitle: 'Test Content',
      }

      // Add a delay to the mock to test loading state
      let resolveUpload: any
      mockUploadImage.mockImplementation(
        () =>
          new Promise(resolve => {
            resolveUpload = resolve
          })
      )

      // Start the operation
      let createPromise: Promise<any>
      act(() => {
        createPromise = result.current.createImageContent(params)
      })

      // Check loading state is true while operation is in progress
      expect(result.current.loading).toBe(true)

      // Complete the upload
      await act(async () => {
        resolveUpload([
          {
            id: 'upload-123',
            imageUrl: 'https://example.com/uploaded-image.jpg',
          },
        ])
        await createPromise
      })

      // Loading should be false after completion
      expect(result.current.loading).toBe(false)
    })

    it('throws error for missing file type', async () => {
      const {result} = renderHook(() => useCreateImageContent())

      // Create a file without a type
      const imageFile = new File(['test'], 'test.jpg')
      Object.defineProperty(imageFile, 'type', {value: undefined})

      const params = {
        image: imageFile,
        contentTitle: 'Test Content',
      }

      await act(async () => {
        await expect(result.current.createImageContent(params)).rejects.toThrow(
          'Unable to determine file type'
        )
      })

      expect(mockUploadImage).not.toHaveBeenCalled()
      expect(mockCreateContent).not.toHaveBeenCalled()
    })

    it('throws error for non-image file type', async () => {
      const {result} = renderHook(() => useCreateImageContent())

      const textFile = new File(['test'], 'test.txt', {type: 'text/plain'})
      const params = {
        image: textFile,
        contentTitle: 'Test Content',
      }

      await act(async () => {
        await expect(result.current.createImageContent(params)).rejects.toThrow(
          'Invalid file type: must be an image'
        )
      })

      expect(mockUploadImage).not.toHaveBeenCalled()
      expect(mockCreateContent).not.toHaveBeenCalled()
    })

    it('throws error when image upload fails', async () => {
      const {result} = renderHook(() => useCreateImageContent())

      // Mock upload to return no URL
      mockUploadImage.mockResolvedValue([
        {
          id: 'upload-123',
          imageUrl: undefined,
          resourceUrl: 'https://example.com/resource/123',
        },
      ])

      const imageFile = new File(['test'], 'test.jpg', {type: 'image/jpeg'})
      const params = {
        image: imageFile,
        contentTitle: 'Test Content',
      }

      await act(async () => {
        await expect(result.current.createImageContent(params)).rejects.toThrow(
          'Image upload failed'
        )
      })

      expect(mockUploadImage).toHaveBeenCalledWith(imageFile)
      expect(mockCreateContent).not.toHaveBeenCalled()
    })

    it('handles content creation with null visibility', async () => {
      const {result} = renderHook(() => useCreateImageContent())

      const imageFile = new File(['test'], 'test.jpg', {type: 'image/jpeg'})
      const params = {
        image: imageFile,
        contentTitle: 'Test Content',
        visibility: null,
      }

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

      expect(mockCreateContent).toHaveBeenCalledWith({
        title: 'Test Content',
        imageUrl: 'https://example.com/uploaded-image.jpg',
        visibility: null,
      })
    })

    it('returns user errors from content creation', async () => {
      const {result} = renderHook(() => useCreateImageContent())

      mockCreateContent.mockResolvedValue({
        data: {
          publicId: 'content-123',
          title: 'Test Content',
        },
        userErrors: [
          {
            field: 'visibility',
            message: 'Invalid visibility',
          },
        ],
      })

      const imageFile = new File(['test'], 'test.jpg', {type: 'image/jpeg'})
      const params = {
        image: imageFile,
        contentTitle: 'Test Content',
      }

      await act(async () => {
        const contentResult = await result.current.createImageContent(params)

        expect(contentResult.userErrors).toEqual([
          {
            field: 'visibility',
            message: 'Invalid visibility',
          },
        ])
      })
    })
  })

  describe('Error Handling', () => {
    it('handles upload error properly', async () => {
      const {result} = renderHook(() => useCreateImageContent())

      mockUploadImage.mockRejectedValue(new Error('Upload failed'))

      const imageFile = new File(['test'], 'test.jpg', {type: 'image/jpeg'})
      const params = {
        image: imageFile,
        contentTitle: 'Test Content',
      }

      // Check that error is thrown
      await act(async () => {
        await expect(result.current.createImageContent(params)).rejects.toThrow(
          'Upload failed'
        )
      })

      // Loading state is only managed during successful operations
      // The hook doesn't reset loading on error since it's controlled by the consumer
    })

    it('handles content creation error properly', async () => {
      const {result} = renderHook(() => useCreateImageContent())

      mockCreateContent.mockRejectedValue(new Error('Creation failed'))

      const imageFile = new File(['test'], 'test.jpg', {type: 'image/jpeg'})
      const params = {
        image: imageFile,
        contentTitle: 'Test Content',
      }

      // Check that error is thrown
      await act(async () => {
        await expect(result.current.createImageContent(params)).rejects.toThrow(
          'Creation failed'
        )
      })

      // Loading state is only managed during successful operations
      // The hook doesn't reset loading on error since it's controlled by the consumer
    })
  })

  describe('Stability', () => {
    it('maintains function reference stability across renders', () => {
      const {result, rerender} = renderHook(() => useCreateImageContent())

      const firstRender = result.current.createImageContent
      rerender()
      const secondRender = result.current.createImageContent

      // Function should maintain reference equality
      expect(firstRender).toBe(secondRender)
    })
  })
})
