import { describe, it, expect, beforeEach, vi, afterEach } from 'vitest'
import { setActivePinia, createPinia } from 'pinia'
import { useUploadStore } from '@/stores/upload'
import type { StickerLayer } from '@/types'

const makeSticker = (overrides: Partial<StickerLayer> = {}): StickerLayer => ({
  id: 'sticker-1',
  type: 'emoji',
  x: 100,
  y: 100,
  scale: 1,
  rotation: 0,
  ...overrides,
})

describe('upload store', () => {
  beforeEach(() => {
    vi.unstubAllGlobals()
    // happy-dom has URL.createObjectURL as a stub; ensure it returns something predictable
    vi.stubGlobal('URL', {
      createObjectURL: vi.fn(() => 'blob:mock-url'),
      revokeObjectURL: vi.fn(),
    })
    setActivePinia(createPinia())
  })

  afterEach(() => {
    // unstubAllGlobals is handled in beforeEach and globally in setup.ts
  })

  it('init sets originalFile and originalUrl', () => {
    const store = useUploadStore()
    const file = new File(['data'], 'photo.jpg', { type: 'image/jpeg' })

    store.init(file)

    // Pinia wraps refs in a reactive proxy, use toStrictEqual for value equality
    expect(store.originalFile).toStrictEqual(file)
    expect(store.originalUrl).toBe('blob:mock-url')
  })

  it('init with deviceId sets contextDeviceId and selectedDeviceIds', () => {
    const store = useUploadStore()
    const file = new File(['data'], 'photo.jpg', { type: 'image/jpeg' })

    store.init(file, 7)

    expect(store.contextDeviceId).toBe(7)
    expect(store.selectedDeviceIds).toEqual([7])
  })

  it('init without deviceId leaves selectedDeviceIds empty', () => {
    const store = useUploadStore()
    const file = new File(['data'], 'photo.jpg')

    store.init(file)

    expect(store.contextDeviceId).toBeNull()
    expect(store.selectedDeviceIds).toEqual([])
  })

  it('setCrop stores croppedBlob, cropParams, and cropOrientation', () => {
    const store = useUploadStore()
    const file = new File(['data'], 'photo.jpg')
    store.init(file)

    const blob = new Blob(['crop'], { type: 'image/jpeg' })
    const params = { natX: 0, natY: 0, natW: 200, natH: 200 }

    store.setCrop(blob, params, 'portrait')

    // Pinia wraps refs in a reactive proxy, use toStrictEqual for value equality
    expect(store.croppedBlob).toStrictEqual(blob)
    expect(store.croppedUrl).toBe('blob:mock-url')
    expect(store.cropParams).toEqual(params)
    expect(store.cropOrientation).toBe('portrait')
  })

  it('addSticker appends to stickers', () => {
    const store = useUploadStore()
    const file = new File(['data'], 'photo.jpg')
    store.init(file)

    store.addSticker(makeSticker({ id: 'a' }))
    store.addSticker(makeSticker({ id: 'b' }))

    expect(store.stickers).toHaveLength(2)
    expect(store.stickers[0].id).toBe('a')
    expect(store.stickers[1].id).toBe('b')
  })

  it('updateSticker patches matching sticker', () => {
    const store = useUploadStore()
    const file = new File(['data'], 'photo.jpg')
    store.init(file)

    store.addSticker(makeSticker({ id: 'a', x: 10 }))
    store.updateSticker('a', { x: 99 })

    expect(store.stickers[0].x).toBe(99)
  })

  it('updateSticker leaves non-matching stickers unchanged', () => {
    const store = useUploadStore()
    const file = new File(['data'], 'photo.jpg')
    store.init(file)

    store.addSticker(makeSticker({ id: 'a', x: 10 }))
    store.addSticker(makeSticker({ id: 'b', x: 20 }))
    store.updateSticker('a', { x: 99 })

    expect(store.stickers[1].x).toBe(20)
  })

  it('removeSticker removes by id', () => {
    const store = useUploadStore()
    const file = new File(['data'], 'photo.jpg')
    store.init(file)

    store.addSticker(makeSticker({ id: 'a' }))
    store.addSticker(makeSticker({ id: 'b' }))
    store.removeSticker('a')

    expect(store.stickers).toHaveLength(1)
    expect(store.stickers[0].id).toBe('b')
  })

  it('initEdit fetches the original blob and seeds edit state from the image', async () => {
    const store = useUploadStore()
    const blob = new Blob(['orig'], { type: 'image/jpeg' })
    vi.stubGlobal('fetch', vi.fn().mockResolvedValue({
      blob: () => Promise.resolve(blob),
    }))

    const image = {
      id: 42,
      originalFilename: 'orig.jpg',
      originalUrl: '/o/42.jpg',
      thumbnailUrl: '/t/42.jpg',
      uploadedAt: '2026-01-01T00:00:00Z',
      approvedDeviceIds: [3, 4],
      cropParams: { natX: 0, natY: 0, natW: 100, natH: 100 },
      cropOrientation: 'portrait',
      stickerState: [makeSticker({ id: 's1' })],
    } as any

    await store.initEdit(image, 7)

    expect(store.editingImageId).toBe(42)
    expect(store.cropParams).toEqual(image.cropParams)
    expect(store.cropOrientation).toBe('portrait')
    expect(store.stickers).toHaveLength(1)
    expect(store.stickers[0].id).toBe('s1')
    expect(store.selectedDeviceIds).toEqual([3, 4])
    expect(store.contextDeviceId).toBe(7)
    expect(store.originalUrl).toBe('blob:mock-url')
  })

  it('initEdit handles images with no cropParams or stickers', async () => {
    const store = useUploadStore()
    const blob = new Blob(['orig'])
    vi.stubGlobal('fetch', vi.fn().mockResolvedValue({
      blob: () => Promise.resolve(blob),
    }))

    const image = {
      id: 1,
      originalFilename: 'a.jpg',
      originalUrl: '/o/1.jpg',
      thumbnailUrl: '/t/1.jpg',
      uploadedAt: '',
      approvedDeviceIds: [],
      cropParams: null,
      cropOrientation: null,
      stickerState: null,
    } as any

    await store.initEdit(image)

    expect(store.cropParams).toBeNull()
    expect(store.cropOrientation).toBeNull()
    expect(store.stickers).toEqual([])
    expect(store.contextDeviceId).toBeNull()
  })

  it('setCrop revokes a previous croppedUrl before assigning a new one', () => {
    const store = useUploadStore()
    const file = new File(['data'], 'photo.jpg')
    store.init(file)
    const params = { natX: 0, natY: 0, natW: 1, natH: 1 }

    store.setCrop(new Blob(['a']), params, 'landscape')
    const url = store.croppedUrl
    expect(url).toBe('blob:mock-url')
    // Second call must revoke the previous URL before assigning the new one
    store.setCrop(new Blob(['b']), params, 'portrait')
    expect(URL.revokeObjectURL).toHaveBeenCalledWith(url)
  })

  it('cleanup resets all state', () => {
    const store = useUploadStore()
    const file = new File(['data'], 'photo.jpg')
    store.init(file, 5)
    store.addSticker(makeSticker())

    store.cleanup()

    expect(store.originalFile).toBeNull()
    expect(store.originalUrl).toBeNull()
    expect(store.croppedBlob).toBeNull()
    expect(store.croppedUrl).toBeNull()
    expect(store.cropParams).toBeNull()
    expect(store.cropOrientation).toBeNull()
    expect(store.stickers).toHaveLength(0)
    expect(store.contextDeviceId).toBeNull()
    expect(store.selectedDeviceIds).toEqual([])
    expect(store.editingImageId).toBeNull()
  })
})
