import { renderHook, waitFor } from '@testing-library/react';
import { SWRConfig } from 'swr';
import { describe, expect, it, vi, beforeEach } from 'vitest';
import { fetchClient } from '@ablogcms/fetch-client';
import type { AcmsContext } from '@ablogcms/url/types';
import useEntryTags from './use-entry-tags';

// テストごとに独立したSWRキャッシュを使うためのラッパー（キャッシュ共有によるテスト間の汚染を防ぐ）
const wrapper = ({ children }: { children: React.ReactNode }) => (
  <SWRConfig value={{ provider: () => new Map() }}>{children}</SWRConfig>
);

vi.mock('@ablogcms/fetch-client', () => ({
  fetchClient: { get: vi.fn() },
}));

describe('useEntryTags', () => {
  beforeEach(() => {
    vi.mocked(fetchClient.get).mockReset();
    (window as unknown as { ACMS: { Library: unknown } }).ACMS.Library = {
      acmsLink: vi.fn((context: AcmsContext) => {
        const tagQuery = context.tag && context.tag.length > 0 ? `?tag=${context.tag.join(',')}` : '';
        return `ajax/entry-tags.json${tagQuery}`;
      }),
    };
  });

  it('選択中タグが無い場合は全タグ一覧を取得する', async () => {
    vi.mocked(fetchClient.get).mockResolvedValueOnce({
      data: ['東京', '大阪', '観光'],
      status: 200,
      statusText: 'OK',
      headers: new Headers(),
    });

    const { result } = renderHook(() => useEntryTags({}), { wrapper });

    await waitFor(() => expect(result.current.isLoading).toBe(false));

    expect(result.current.tags).toEqual(['東京', '大阪', '観光']);
    expect(fetchClient.get).toHaveBeenCalledWith('ajax/entry-tags.json');
  });

  it('選択中タグを渡すと紐づくタグ一覧を取得する', async () => {
    vi.mocked(fetchClient.get).mockResolvedValueOnce({
      data: ['観光', 'グルメ'],
      status: 200,
      statusText: 'OK',
      headers: new Headers(),
    });

    const { result } = renderHook(() => useEntryTags({ tag: ['東京'] }), { wrapper });

    await waitFor(() => expect(result.current.isLoading).toBe(false));

    expect(result.current.tags).toEqual(['観光', 'グルメ']);
    expect(fetchClient.get).toHaveBeenCalledWith('ajax/entry-tags.json?tag=東京');
  });

  it('選択中タグが変わると再取得する', async () => {
    vi.mocked(fetchClient.get)
      .mockResolvedValueOnce({
        data: ['観光', 'グルメ'],
        status: 200,
        statusText: 'OK',
        headers: new Headers(),
      })
      .mockResolvedValueOnce({
        data: ['海外'],
        status: 200,
        statusText: 'OK',
        headers: new Headers(),
      });

    const { result, rerender } = renderHook(({ tag }: { tag: string[] }) => useEntryTags({ tag }), {
      wrapper,
      initialProps: { tag: ['東京'] },
    });

    await waitFor(() => expect(result.current.isLoading).toBe(false));
    expect(result.current.tags).toEqual(['観光', 'グルメ']);

    rerender({ tag: ['大阪'] });

    await waitFor(() => expect(result.current.tags).toEqual(['海外']));
    expect(fetchClient.get).toHaveBeenCalledTimes(2);
  });

  it('レスポンスが配列でない場合は空配列を返す', async () => {
    vi.mocked(fetchClient.get).mockResolvedValueOnce({
      data: null,
      status: 200,
      statusText: 'OK',
      headers: new Headers(),
    });

    const { result } = renderHook(() => useEntryTags({}), { wrapper });

    await waitFor(() => expect(result.current.isLoading).toBe(false));

    expect(result.current.tags).toEqual([]);
  });
});
