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

import PostListPage from '@/pages/posts/PostListPage'
import { buildPost, buildTag, buildWikiPage } from '@/test/factories'
import { renderWithProviders } from '@/test/render'
import { CLIENT_SETTINGS_STORAGE_KEY } from '@/lib/settings'

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

const wikiApi = vi.hoisted (() => ({
  fetchWikiPageByTitle: vi.fn (),
  fetchWikiPages:       vi.fn (),
}))

vi.mock ('@/lib/posts', () => postsApi)
vi.mock ('@/lib/wiki', () => wikiApi)

describe ('PostListPage', () => {
  beforeEach (() => {
    localStorage.clear ()
    vi.clearAllMocks ()
  })

  it ('loads posts from the current query and renders the plaza tab', async () => {
    const tag = buildTag ({ name: '虹夏' })
    postsApi.fetchPosts.mockResolvedValueOnce ({
      posts: [buildPost ({ id: 5, title: '投稿5', tags: [tag] })],
      count: 1,
    })
    wikiApi.fetchWikiPageByTitle.mockResolvedValueOnce (buildWikiPage ({ title: '虹夏' }))
    wikiApi.fetchWikiPages.mockResolvedValueOnce ([buildWikiPage ({ title: '虹夏' })])

    renderWithProviders (<PostListPage/>, { route: '/posts?tags=%E8%99%B9%E5%A4%8F&page=2' })

    await waitFor (() => {
      expect (postsApi.fetchPosts).toHaveBeenCalledWith (
          expect.objectContaining ({
            tags:  '虹夏',
            match: 'all',
            page:  2,
            limit: 20,
            order: 'original_created_at:desc',
          }),
      )
    })
    expect (await screen.findByRole ('link', { name: '投稿5' })).toHaveAttribute (
        'href',
        '/posts/5',
    )
    expect (screen.getByText ('広場')).toBeInTheDocument ()
  })

  it ('shows the empty state when loading finishes without posts', async () => {
    postsApi.fetchPosts.mockResolvedValueOnce ({ posts: [], count: 0 })

    renderWithProviders (<PostListPage/>, { route: '/posts' })

    expect (await screen.findByText ('広場には何もありませんよ．')).toBeInTheDocument ()
  })

  it ('renders the wiki tab for single-tag pages', async () => {
    postsApi.fetchPosts.mockResolvedValueOnce ({ posts: [], count: 0 })
    wikiApi.fetchWikiPageByTitle.mockResolvedValueOnce (
        buildWikiPage ({ title: '虹夏', body: 'Wiki body' }),
    )
    wikiApi.fetchWikiPages.mockResolvedValueOnce ([buildWikiPage ({ title: '虹夏' })])

    renderWithProviders (<PostListPage/>, { route: '/posts?tags=%E8%99%B9%E5%A4%8F' })

    fireEvent.click (await screen.findByText ('Wiki'))

    expect (await screen.findByText ('Wiki body')).toBeInTheDocument ()
    expect (screen.getByRole ('link', { name: 'Wiki を見る' })).toHaveAttribute (
        'href',
        '/wiki/%E8%99%B9%E5%A4%8F',
    )
  })

  it ('uses stored list limit but keeps the fixed plaza order', async () => {
    localStorage.setItem (
        CLIENT_SETTINGS_STORAGE_KEY,
        JSON.stringify ({ lists: { postList: { limit: 50, order: 'title:asc' } } }))
    postsApi.fetchPosts.mockResolvedValueOnce ({ posts: [], count: 0 })

    renderWithProviders (<PostListPage/>, { route: '/posts' })

    await waitFor (() => {
      expect (postsApi.fetchPosts).toHaveBeenCalledWith (
          expect.objectContaining ({
            limit: 50,
            order: 'original_created_at:desc',
          }),
      )
    })
  })

  it ('updates the limit from the tab right addon and resets to page 1', async () => {
    postsApi.fetchPosts.mockResolvedValue ({ posts: [], count: 0 })

    renderWithProviders (<PostListPage/>, { route: '/posts?page=3' })

    fireEvent.change (await screen.findByLabelText ('件数'), { target: { value: '100' } })

    await waitFor (() => {
      expect (postsApi.fetchPosts).toHaveBeenLastCalledWith (
          expect.objectContaining ({ page: 1, limit: 100 }),
      )
    })
  })
})
