import { describe, expect, it, vi, beforeEach, afterEach } from 'vitest'
import { flushPromises, mount } from '@vue/test-utils'
import { defineComponent, ref } from 'vue'
import SoraGeneratePage from '../SoraGeneratePage.vue'
import type { SoraGeneration, QuotaInfo, GenerateRequest } from '@/api/sora'

// 使用 vi.hoisted 确保 mock 函数在 mock 工厂函数执行前定义
const {
  mockGenerate,
  mockGetGeneration,
  mockCancelGeneration,
  mockDeleteGeneration,
  mockSaveToStorage,
  mockGetQuota,
  mockGetModels,
  mockListGenerations
} = vi.hoisted(() => ({
  mockGenerate: vi.fn(),
  mockGetGeneration: vi.fn(),
  mockCancelGeneration: vi.fn(),
  mockDeleteGeneration: vi.fn(),
  mockSaveToStorage: vi.fn(),
  mockGetQuota: vi.fn(),
  mockGetModels: vi.fn(),
  mockListGenerations: vi.fn()
}))

// Mock SoraProgressCard component
vi.mock('../SoraProgressCard.vue', () => ({
  default: defineComponent({
    name: 'SoraProgressCard',
    props: ['generation'],
    emits: ['cancel', 'delete', 'save', 'retry'],
    template: '<div class="sora-progress-card" :data-id="generation.id">{{ generation.status }}</div>'
  })
}))

// Mock SoraPromptBar component - 必须暴露 fillPrompt 和 reset 方法
vi.mock('../SoraPromptBar.vue', () => ({
  default: defineComponent({
    name: 'SoraPromptBar',
    props: ['generating', 'activeTaskCount', 'maxConcurrentTasks'],
    emits: ['generate'],
    setup(_, { expose }) {
      const promptText = ref('')
      const fillPrompt = (text: string) => {
        promptText.value = text
      }
      const reset = () => {
        promptText.value = ''
      }
      expose({ fillPrompt, reset })
      return { promptText, fillPrompt, reset }
    },
    template: '<div class="sora-prompt-bar"><slot /></div>'
  })
}))

// Mock API
vi.mock('@/api/sora', () => ({
  default: {
    generate: mockGenerate,
    getGeneration: mockGetGeneration,
    cancelGeneration: mockCancelGeneration,
    deleteGeneration: mockDeleteGeneration,
    saveToStorage: mockSaveToStorage,
    getQuota: mockGetQuota,
    getModels: mockGetModels,
    listGenerations: mockListGenerations,
    getStorageStatus: vi.fn().mockResolvedValue({ s3_enabled: true, s3_healthy: true })
  }
}))

// Mock vue-i18n
vi.mock('vue-i18n', async () => {
  const actual = await vi.importActual<typeof import('vue-i18n')>('vue-i18n')
  return {
    ...actual,
    useI18n: () => ({
      t: (key: string) => key
    })
  }
})

// Mock window.Notification
vi.stubGlobal('Notification', {
  permission: 'default',
  requestPermission: vi.fn().mockResolvedValue('granted')
})

function createMockGeneration(overrides: Partial<SoraGeneration> = {}): SoraGeneration {
  return {
    id: 1,
    user_id: 1,
    model: 'sora2',
    prompt: 'Test prompt',
    media_type: 'video',
    status: 'completed',
    storage_type: 's3',
    media_url: 'https://example.com/video.mp4',
    media_urls: [],
    s3_object_keys: [],
    file_size_bytes: 1024 * 1024,
    error_message: '',
    created_at: '2024-01-01T00:00:00Z',
    ...overrides
  }
}

function createMockQuota(overrides: Partial<QuotaInfo> = {}): QuotaInfo {
  return {
    quota_bytes: 10 * 1024 * 1024 * 1024,
    used_bytes: 1 * 1024 * 1024 * 1024,
    available_bytes: 9 * 1024 * 1024 * 1024,
    quota_source: 'user',
    ...overrides
  }
}

describe('SoraGeneratePage', () => {
  beforeEach(() => {
    vi.clearAllMocks()
    vi.useFakeTimers()

    mockGetQuota.mockResolvedValue(createMockQuota())
    mockGetModels.mockResolvedValue([
      { id: 'sora2', name: 'Sora 2', type: 'video', orientations: ['landscape', 'portrait'], durations: [10, 15, 25] }
    ])
    mockListGenerations.mockResolvedValue({ data: [], total: 0, page: 1 })
  })

  afterEach(() => {
    vi.useRealTimers()
  })

  describe('初始渲染', () => {
    it('无活跃任务时显示欢迎区域', async () => {
      const wrapper = mount(SoraGeneratePage)

      await flushPromises()

      expect(wrapper.find('.sora-welcome-section').exists()).toBe(true)
      expect(wrapper.find('.sora-welcome-title').text()).toBe('sora.welcomeTitle')
    })

    it('无活跃任务时显示示例提示词', async () => {
      const wrapper = mount(SoraGeneratePage)

      await flushPromises()

      const examplePrompts = wrapper.findAll('.sora-example-prompt')
      expect(examplePrompts.length).toBeGreaterThan(0)
    })

    it('有活跃任务时隐藏欢迎区域', async () => {
      mockListGenerations.mockResolvedValue({
        data: [createMockGeneration({ status: 'generating' })],
        total: 1,
        page: 1
      })

      const wrapper = mount(SoraGeneratePage)

      await flushPromises()

      expect(wrapper.find('.sora-welcome-section').exists()).toBe(false)
      expect(wrapper.find('.sora-task-cards').exists()).toBe(true)
    })
  })

  describe('生成流程', () => {
    it('点击示例提示词填充输入框', async () => {
      const wrapper = mount(SoraGeneratePage)

      await flushPromises()

      const firstExample = wrapper.find('.sora-example-prompt')
      // 点击不应抛出错误（fillPrompt 方法已被 mock）
      await firstExample.trigger('click')
      await flushPromises()

      expect(wrapper.exists()).toBe(true)
    })

    it('成功提交生成请求', async () => {
      mockGenerate.mockResolvedValue({ generation_id: 123 })
      mockGetGeneration.mockResolvedValue(createMockGeneration({ id: 123, status: 'pending' }))

      const wrapper = mount(SoraGeneratePage)

      await flushPromises()

      // 直接通过 findComponent 获取 SoraPromptBar 并触发 generate 事件
      const promptBar = wrapper.findComponent({ name: 'SoraPromptBar' })
      const generateReq: GenerateRequest = { model: 'sora2', prompt: 'Test prompt', media_type: 'video' }
      await promptBar.vm.$emit('generate', generateReq)
      await flushPromises()

      expect(mockGenerate).toHaveBeenCalledWith(generateReq)
    })

    it('生成失败时显示错误提示', async () => {
      const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {})
      mockGenerate.mockRejectedValue(new Error('Generation failed'))

      // Mock alert
      vi.stubGlobal('alert', vi.fn())

      const wrapper = mount(SoraGeneratePage)

      await flushPromises()

      const promptBar = wrapper.findComponent({ name: 'SoraPromptBar' })
      const generateReq: GenerateRequest = { model: 'sora2', prompt: 'Test', media_type: 'video' }
      await promptBar.vm.$emit('generate', generateReq)
      await flushPromises()

      expect(consoleSpy).toHaveBeenCalled()
      consoleSpy.mockRestore()
    })
  })

  describe('任务管理', () => {
    it('取消任务', async () => {
      mockCancelGeneration.mockResolvedValue({})
      mockListGenerations.mockResolvedValue({
        data: [createMockGeneration({ id: 1, status: 'generating' })],
        total: 1,
        page: 1
      })

      const wrapper = mount(SoraGeneratePage)

      await flushPromises()

      const progressCard = wrapper.findComponent({ name: 'SoraProgressCard' })
      await progressCard.vm.$emit('cancel', 1)
      await flushPromises()

      expect(mockCancelGeneration).toHaveBeenCalledWith(1)
    })

    it('删除任务', async () => {
      mockDeleteGeneration.mockResolvedValue({})
      mockListGenerations.mockResolvedValue({
        data: [createMockGeneration({ id: 1 })],
        total: 1,
        page: 1
      })

      const wrapper = mount(SoraGeneratePage)

      await flushPromises()

      const progressCard = wrapper.findComponent({ name: 'SoraProgressCard' })
      await progressCard.vm.$emit('delete', 1)
      await flushPromises()

      expect(mockDeleteGeneration).toHaveBeenCalledWith(1)
    })

    it('保存到存储', async () => {
      mockSaveToStorage.mockResolvedValue({ message: 'Saved' })
      mockGetGeneration.mockResolvedValue(createMockGeneration({ id: 1, storage_type: 's3' }))
      mockListGenerations.mockResolvedValue({
        data: [createMockGeneration({ id: 1 })],
        total: 1,
        page: 1
      })

      const wrapper = mount(SoraGeneratePage)

      await flushPromises()

      const progressCard = wrapper.findComponent({ name: 'SoraProgressCard' })
      await progressCard.vm.$emit('save', 1)
      await flushPromises()

      expect(mockSaveToStorage).toHaveBeenCalledWith(1)
    })
  })

  describe('任务计数', () => {
    it('计算活跃任务数量', async () => {
      mockListGenerations.mockResolvedValue({
        data: [
          createMockGeneration({ id: 1, status: 'pending' }),
          createMockGeneration({ id: 2, status: 'generating' }),
          createMockGeneration({ id: 3, status: 'completed' })
        ],
        total: 3,
        page: 1
      })

      const wrapper = mount(SoraGeneratePage)

      await flushPromises()

      // activeTaskCount 应该只计算 pending 和 generating
      const promptBar = wrapper.findComponent({ name: 'SoraPromptBar' })
      expect(promptBar.props('activeTaskCount')).toBe(2)
    })

    it('触发 task-count-change 事件', async () => {
      mockListGenerations.mockResolvedValue({
        data: [createMockGeneration({ status: 'generating' })],
        total: 1,
        page: 1
      })

      const wrapper = mount(SoraGeneratePage)

      await flushPromises()

      // 事件应该在 watch 中触发
      expect(wrapper.emitted('task-count-change')).toBeTruthy()
    })
  })

  describe('轮询机制', () => {
    it('启动轮询检查任务状态', async () => {
      mockGenerate.mockResolvedValue({ generation_id: 123 })
      // 使用接近当前时间的时间戳，确保轮询间隔为 3 秒
      const now = new Date().toISOString()
      mockGetGeneration.mockResolvedValue(createMockGeneration({ id: 123, status: 'generating', created_at: now }))

      const wrapper = mount(SoraGeneratePage)

      await flushPromises()

      const promptBar = wrapper.findComponent({ name: 'SoraPromptBar' })
      const generateReq: GenerateRequest = { model: 'sora2', prompt: 'Test', media_type: 'video' }
      await promptBar.vm.$emit('generate', generateReq)
      await flushPromises()

      // 第一次 getGeneration 在 handleGenerate 中
      expect(mockGetGeneration).toHaveBeenCalledTimes(1)

      // 快进轮询定时器 - 对于刚创建的任务，轮询间隔为 3 秒
      vi.advanceTimersByTime(3000)
      await flushPromises()

      // 轮询应该再次调用 getGeneration
      expect(mockGetGeneration).toHaveBeenCalledTimes(2)
    })
  })

  describe('边界条件', () => {
    it('API 错误时不会崩溃', async () => {
      const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {})
      mockGetQuota.mockRejectedValue(new Error('Network error'))

      const wrapper = mount(SoraGeneratePage)

      await flushPromises()

      expect(wrapper.exists()).toBe(true)
      consoleSpy.mockRestore()
    })

    it('组件卸载时清理定时器', async () => {
      mockGenerate.mockResolvedValue({ generation_id: 123 })
      mockGetGeneration.mockResolvedValue(createMockGeneration({ id: 123, status: 'generating' }))

      const wrapper = mount(SoraGeneratePage)

      await flushPromises()

      const promptBar = wrapper.findComponent({ name: 'SoraPromptBar' })
      const generateReq: GenerateRequest = { model: 'sora2', prompt: 'Test', media_type: 'video' }
      await promptBar.vm.$emit('generate', generateReq)
      await flushPromises()

      // 卸载组件
      wrapper.unmount()

      // 清理后不应该有内存泄漏
      vi.advanceTimersByTime(10000)
      expect(true).toBe(true)
    })
  })
})
