import { beforeEach, describe, expect, it, vi } from 'vitest'

const deleteObjectMock = vi.hoisted(() => vi.fn())
const resolveStorageKeyFromMediaValueMock = vi.hoisted(() => vi.fn())
const prismaMock = vi.hoisted(() => ({
  novelPromotionLocation: {
    findUnique: vi.fn(),
    update: vi.fn(),
  },
  locationImage: {
    update: vi.fn(),
    deleteMany: vi.fn(),
  },
  $transaction: vi.fn(),
}))

vi.mock('@/lib/prisma', () => ({
  prisma: prismaMock,
}))

vi.mock('@/lib/storage', () => ({
  deleteObject: deleteObjectMock,
}))

vi.mock('@/lib/media/service', () => ({
  resolveStorageKeyFromMediaValue: resolveStorageKeyFromMediaValueMock,
}))

describe('project location-backed selection service', () => {
  beforeEach(() => {
    vi.clearAllMocks()
    prismaMock.$transaction.mockImplementation(async (
      callback: (tx: {
        locationImage: {
          update: typeof prismaMock.locationImage.update
          deleteMany: typeof prismaMock.locationImage.deleteMany
        }
        novelPromotionLocation: {
          update: typeof prismaMock.novelPromotionLocation.update
        }
      }) => Promise<void>,
    ) => callback({
      locationImage: prismaMock.locationImage,
      novelPromotionLocation: prismaMock.novelPromotionLocation,
    }))
    resolveStorageKeyFromMediaValueMock.mockImplementation(async (value: string) => `key:${value}`)
    deleteObjectMock.mockResolvedValue(undefined)
    prismaMock.locationImage.deleteMany.mockResolvedValue({ count: 1 })
    prismaMock.locationImage.update.mockResolvedValue(undefined)
    prismaMock.novelPromotionLocation.update.mockResolvedValue(undefined)
  })

  it('confirms a prop selection by keeping only the selected render', async () => {
    prismaMock.novelPromotionLocation.findUnique.mockResolvedValue({
      id: 'prop-1',
      selectedImageId: 'prop-image-2',
      images: [
        {
          id: 'prop-image-1',
          imageIndex: 0,
          imageUrl: 'https://example.com/prop-1.png',
          isSelected: false,
        },
        {
          id: 'prop-image-2',
          imageIndex: 1,
          imageUrl: 'https://example.com/prop-2.png',
          isSelected: true,
        },
      ],
    })

    const mod = await import('@/lib/assets/services/project-location-backed-selection')

    const result = await mod.confirmProjectLocationBackedSelection('prop-1')

    expect(result).toEqual({ success: true })
    expect(resolveStorageKeyFromMediaValueMock).toHaveBeenCalledWith('https://example.com/prop-1.png')
    expect(deleteObjectMock).toHaveBeenCalledWith('key:https://example.com/prop-1.png')
    expect(prismaMock.locationImage.deleteMany).toHaveBeenCalledWith({
      where: {
        locationId: 'prop-1',
        id: { not: 'prop-image-2' },
      },
    })
    expect(prismaMock.locationImage.update).toHaveBeenCalledWith({
      where: { id: 'prop-image-2' },
      data: {
        imageIndex: 0,
        isSelected: true,
      },
    })
    expect(prismaMock.novelPromotionLocation.update).toHaveBeenCalledWith({
      where: { id: 'prop-1' },
      data: { selectedImageId: 'prop-image-2' },
    })
  })

  it('fails explicitly when confirming without a selected prop render', async () => {
    prismaMock.novelPromotionLocation.findUnique.mockResolvedValue({
      id: 'prop-1',
      selectedImageId: null,
      images: [
        {
          id: 'prop-image-1',
          imageIndex: 0,
          imageUrl: 'https://example.com/prop-1.png',
          isSelected: false,
        },
        {
          id: 'prop-image-2',
          imageIndex: 1,
          imageUrl: 'https://example.com/prop-2.png',
          isSelected: false,
        },
      ],
    })

    const mod = await import('@/lib/assets/services/project-location-backed-selection')

    await expect(mod.confirmProjectLocationBackedSelection('prop-1')).rejects.toMatchObject({
      code: 'INVALID_PARAMS',
    })
    expect(prismaMock.locationImage.deleteMany).not.toHaveBeenCalled()
    expect(deleteObjectMock).not.toHaveBeenCalled()
  })
})
