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

import PostNewPage from '@/pages/posts/PostNewPage'
import { buildUser } from '@/test/factories'
import { renderWithProviders } from '@/test/render'

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

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

vi.mock ('@/lib/api', () => api)
vi.mock ('@/components/ui/use-toast', () => toastApi)

describe ('PostNewPage', () => {
  beforeEach (() => {
    vi.clearAllMocks ()
    api.isApiError.mockReturnValue (false)
  })

  it ('blocks guests', () => {
    renderWithProviders (<PostNewPage user={buildUser ({ role: 'guest' })}/>)

    expect (screen.getByText ('403')).toBeInTheDocument ()
  })

  it ('submits a new post with manual title and thumbnail settings', async () => {
    api.apiPost.mockResolvedValueOnce ({})
    api.apiGet.mockResolvedValue ([])

    renderWithProviders (<PostNewPage user={buildUser ({ role: 'member' })}/>)

    const checkboxes = screen.getAllByRole ('checkbox', { name: '自動' })
    fireEvent.click (checkboxes[0])
    fireEvent.click (checkboxes[1])

    const textboxes = screen.getAllByRole ('textbox')
    fireEvent.change (textboxes[0], { target: { value: 'https://example.com/post' } })
    fireEvent.change (textboxes[1], { target: { value: '投稿タイトル' } })
    fireEvent.change (textboxes[2], { target: { value: '1 2' } })
    fireEvent.change (textboxes[3], { target: { value: 'tag1 tag2' } })
    fireEvent.click (screen.getByRole ('button', { name: '追加' }))

    await waitFor (() => {
      expect (api.apiPost).toHaveBeenCalledWith (
          '/posts',
          expect.any (FormData),
          { headers: { 'Content-Type': 'multipart/form-data' } },
      )
    })
    const formData = api.apiPost.mock.calls[0]?.[1] as FormData
    expect (formData.get ('url')).toBe ('https://example.com/post')
    expect (formData.get ('title')).toBe ('投稿タイトル')
    expect (formData.get ('parent_post_ids')).toBe ('1 2')
    expect (formData.get ('tags')).toBe ('tag1 tag2')
    expect (toastApi.toast).toHaveBeenCalledWith ({ title: '投稿成功!' })
  })

  it ('shows 422 validation errors for post fields', async () => {
    api.apiGet.mockResolvedValue ([])
    api.isApiError.mockReturnValue (true)
    api.apiPost.mockRejectedValueOnce ({
      response: {
        status: 422,
        data:   {
          type:        'validation_error',
          message:     '入力内容を確認してください．',
          errors:      { tags: ['ニコニコ・タグは直接指定できません．'] },
          base_errors: ['投稿内容を確認してください．'],
        },
      },
    })

    renderWithProviders (<PostNewPage user={buildUser ({ role: 'member' })}/>)

    const checkboxes = screen.getAllByRole ('checkbox', { name: '自動' })
    fireEvent.click (checkboxes[0])
    fireEvent.click (checkboxes[1])

    const textboxes = screen.getAllByRole ('textbox')
    fireEvent.change (textboxes[0], { target: { value: 'https://example.com/post' } })
    fireEvent.change (textboxes[1], { target: { value: '投稿タイトル' } })
    fireEvent.change (textboxes[3], { target: { value: 'nico:nico_tag' } })
    fireEvent.click (screen.getByRole ('button', { name: '追加' }))

    expect (await screen.findByText ('投稿内容を確認してください．')).toBeInTheDocument ()
    expect (screen.getByText ('ニコニコ・タグは直接指定できません．')).toBeInTheDocument ()
    expect (screen.getAllByRole ('textbox')[3]).toHaveAttribute ('aria-invalid', 'true')
  })
})
