import { beforeEach, describe, expect, it, vi } from 'vitest'
import { TASK_STATUS, TASK_TYPE } from '@/lib/task/types'
import { createTask } from '@/lib/task/service'
import { prisma } from '../helpers/prisma'
import { resetBillingState } from '../helpers/db-reset'
import { createTestProject, createTestUser } from '../helpers/billing-fixtures'

const reconcileMock = vi.hoisted(() => ({
  isJobAlive: vi.fn(async () => true),
}))

vi.mock('@/lib/task/reconcile', () => reconcileMock)

describe('regression - task dedupe recovery', () => {
  beforeEach(async () => {
    await resetBillingState()
    vi.clearAllMocks()
    reconcileMock.isJobAlive.mockResolvedValue(true)
  })

  it('replaces locale-less queued task instead of deduping forever', async () => {
    const user = await createTestUser()
    const project = await createTestProject(user.id)
    const stale = await prisma.task.create({
      data: {
        userId: user.id,
        projectId: project.id,
        type: TASK_TYPE.SCRIPT_TO_STORYBOARD_RUN,
        targetType: 'NovelPromotionEpisode',
        targetId: 'episode-regression-1',
        status: TASK_STATUS.QUEUED,
        payload: { episodeId: 'episode-regression-1' },
        dedupeKey: 'script_to_storyboard_run:episode-regression-1',
        queuedAt: new Date(),
      },
    })

    const replacement = await createTask({
      userId: user.id,
      projectId: project.id,
      type: TASK_TYPE.SCRIPT_TO_STORYBOARD_RUN,
      targetType: 'NovelPromotionEpisode',
      targetId: 'episode-regression-1',
      payload: {
        episodeId: 'episode-regression-1',
        meta: { locale: 'zh' },
      },
      dedupeKey: 'script_to_storyboard_run:episode-regression-1',
    })

    expect(replacement.deduped).toBe(false)
    expect(replacement.task.id).not.toBe(stale.id)

    const failedStale = await prisma.task.findUnique({ where: { id: stale.id } })
    expect(failedStale).toMatchObject({
      status: TASK_STATUS.FAILED,
      errorCode: 'TASK_LOCALE_REQUIRED',
      dedupeKey: null,
    })
  })

  it('replaces orphaned queued task when queue job is gone', async () => {
    const user = await createTestUser()
    const project = await createTestProject(user.id)
    const orphan = await prisma.task.create({
      data: {
        userId: user.id,
        projectId: project.id,
        type: TASK_TYPE.VIDEO_PANEL,
        targetType: 'NovelPromotionPanel',
        targetId: 'panel-regression-1',
        status: TASK_STATUS.QUEUED,
        payload: {
          storyboardId: 'storyboard-regression-1',
          panelIndex: 1,
          meta: { locale: 'zh' },
        },
        dedupeKey: 'video_panel:panel-regression-1',
        queuedAt: new Date(),
      },
    })
    reconcileMock.isJobAlive.mockResolvedValue(false)

    const replacement = await createTask({
      userId: user.id,
      projectId: project.id,
      type: TASK_TYPE.VIDEO_PANEL,
      targetType: 'NovelPromotionPanel',
      targetId: 'panel-regression-1',
      payload: {
        storyboardId: 'storyboard-regression-1',
        panelIndex: 1,
        meta: { locale: 'zh' },
      },
      dedupeKey: 'video_panel:panel-regression-1',
    })

    expect(replacement.deduped).toBe(false)
    expect(replacement.task.id).not.toBe(orphan.id)

    const failedOrphan = await prisma.task.findUnique({ where: { id: orphan.id } })
    expect(failedOrphan).toMatchObject({
      status: TASK_STATUS.FAILED,
      errorCode: 'RECONCILE_ORPHAN',
      dedupeKey: null,
    })
  })
})
