import { fireEvent, render, screen, waitFor } from '@testing-library/react'
import { describe, expect, it, vi } from 'vitest'

import PostEditForm from '@/components/PostEditForm'
import { buildPost, buildTag } from '@/test/factories'

const postsApi = vi.hoisted (() => ({
  updatePost: vi.fn (),
}))

const api = vi.hoisted (() => ({
  isApiError: vi.fn (() => false),
}))

const toastApi = vi.hoisted (() => ({
  toast: vi.fn (),
}))

vi.mock ('@/lib/posts', () => postsApi)
vi.mock ('@/lib/api', () => api)
vi.mock ('@/components/ui/use-toast', () => toastApi)
vi.mock ('@/lib/dialogues/useDialogue', () => ({
  default: () => ({
    choice: vi.fn (),
  }),
}))

describe ('PostEditForm', () => {
  it ('submits edited post fields with the current base version', async () => {
    const onSave = vi.fn ()
    const post = buildPost ({
      id:        8,
      versionNo: 4,
      title:     'old',
      tags:      [
        buildTag ({ name: 'general-tag', category: 'general' }),
        buildTag ({ id: 2, name: 'nico-tag', category: 'nico' }),
      ],
      parentPosts: [buildPost ({ id: 2, title: 'parent' })],
    })
    postsApi.updatePost.mockResolvedValueOnce ({
      ...post,
      versionNo: 5,
      title:     'new',
      tags:      [buildTag ({ name: 'new-tag' })],
    })

    render (<PostEditForm post={post} onSave={onSave}/>)

    const [title, parentIds] = screen.getAllByRole ('textbox')
    fireEvent.change (title, { target: { value: 'new' } })
    fireEvent.change (parentIds, { target: { value: '3 4' } })
    fireEvent.submit (screen.getByRole ('button', { name: '更新' }).closest ('form')!)

    await waitFor (() => {
      expect (postsApi.updatePost).toHaveBeenCalledWith (
          expect.objectContaining ({
            id:            8,
            title:         'new',
            parentPostIds: '3 4',
            tags:          'general-tag',
          }),
          { baseVersionNo: 4 },
      )
    })
    expect (onSave).toHaveBeenCalledWith (expect.objectContaining ({ versionNo: 5 }))
    expect (toastApi.toast).toHaveBeenCalledWith ({ description: '更新しました．' })
  })

  it ('preserves duration while the video tag is temporarily removed', () => {
    const post = buildPost ({
      videoMs: 180_500,
      tags:    [
        buildTag ({ id: 1, name: '動画', category: 'general' }),
        buildTag ({ id: 2, name: 'general-tag', category: 'general' }),
      ],
    })

    render (<PostEditForm post={post} onSave={vi.fn ()}/>)

    expect (screen.getByPlaceholderText ('例: 2 / 2.5 / 1:23')).toHaveValue ('180.5')

    const tags = screen.getAllByRole ('textbox')[2]
    fireEvent.change (tags, { target: { value: 'general-tag' } })
    expect (screen.queryByPlaceholderText ('例: 2 / 2.5 / 1:23')).not.toBeInTheDocument ()

    fireEvent.change (tags, {
      target: { value: '動画 general-tag' },
    })
    expect (screen.getByPlaceholderText ('例: 2 / 2.5 / 1:23')).toHaveValue ('180.5')
  })
})
