import { act, render, renderHook } from '@testing-library/react';
import { afterEach, describe, expect, it, vi } from 'vitest';
import { getDataviewBulkActionRegistry } from '@ablogcms/plugins/core';
import useModuleAdminBulkActions, { type GetBulkActionsContext } from './use-module-admin-bulk-actions';
import { bulkChangeStatus } from '../api';

// BlogSelect は SWR 経由の非同期データ取得を伴うため、Button/Select への置き換え確認に
// 無関係な fetch モックを増やさないよう簡易スタブに差し替える。
vi.mock('@features/blog/components/blog-select/blog-select', () => ({
  default: () => <div data-testid="blog-select" />,
}));

vi.mock('../api', () => ({
  bulkChangeBlog: vi.fn(async () => undefined),
  bulkChangeStatus: vi.fn(async () => undefined),
  bulkDelete: vi.fn(async () => undefined),
  exportModules: vi.fn(async () => undefined),
}));

const createContext = (bulkActions: string[]): GetBulkActionsContext => ({
  invalidate: vi.fn(),
  searchParams: new URLSearchParams(),
  bulkActions,
});

const setup = (bulkActions: string[]) => {
  const context = createContext(bulkActions);
  const { result } = renderHook(() => useModuleAdminBulkActions({ getBulkActions: (actions) => actions, context }));
  return result.current;
};

// button.tsx の unstyled + className 合成モデルに従い、acms-admin-btn-admin 系クラスが
// 置き換え前と同一のまま DOM に出ることを固定する特徴付けテスト。
describe('useModuleAdminBulkActions', () => {
  it('status アクションが Select（acms-admin-form-select）とボタンを保持する', () => {
    const actions = setup(['status']);
    const action = actions.find((a) => a.id === 'status')!;
    const { container } = render(<>{action.renderForm!([])}</>);
    const select = container.querySelector('select')!;
    const button = container.querySelector('button')!;
    expect(select).toHaveClass('acms-admin-form-select');
    expect(button).toHaveClass('acms-admin-btn-admin', 'acms-admin-btn-admin-primary', 'acms-admin-margin-left-mini');
  });

  it('blog アクションのボタンが acms-admin-margin-left-mini を含めて維持される', () => {
    const actions = setup(['status']);
    const action = actions.find((a) => a.id === 'blog')!;
    const { container } = render(<>{action.renderForm!([])}</>);
    const button = container.querySelector('button')!;
    expect(button).toHaveClass('acms-admin-btn-admin', 'acms-admin-btn-admin-primary', 'acms-admin-margin-left-mini');
  });

  it('export アクションのボタンが acms-admin-btn-admin acms-admin-btn-admin-primary を保持する', () => {
    const actions = setup(['export']);
    const action = actions.find((a) => a.id === 'export')!;
    const { container } = render(<>{action.renderForm!([])}</>);
    const button = container.querySelector('button')!;
    expect(button).toHaveClass('acms-admin-btn-admin', 'acms-admin-btn-admin-primary');
  });

  it('delete アクションのボタンが acms-admin-btn-admin acms-admin-btn-admin-danger を保持し確認ダイアログを呼ぶ', () => {
    const confirmSpy = vi.spyOn(window, 'confirm').mockReturnValue(true);
    const actions = setup(['delete']);
    const action = actions.find((a) => a.id === 'delete')!;
    const { container } = render(<>{action.renderForm!([])}</>);
    const button = container.querySelector('button')!;
    expect(button).toHaveClass('acms-admin-btn-admin', 'acms-admin-btn-admin-danger');
    confirmSpy.mockRestore();
  });

  describe('ctx.dataview.registerBulkAction("module", ...) レジストリとのマージ', () => {
    afterEach(() => {
      getDataviewBulkActionRegistry().remove('module', 'plugin-bulk-action');
    });

    it('レジストリに登録した一括アクションがコア定義の後ろに追加され getBulkActions に渡る', () => {
      getDataviewBulkActionRegistry().add('module', 'plugin-bulk-action', {
        id: 'plugin-bulk-action',
        label: 'プラグイン一括操作',
      });

      const actions = setup([]);
      expect(actions.map((a) => a.id)).toContain('plugin-bulk-action');
      expect(actions.at(-1)?.id).toBe('plugin-bulk-action');
    });

    it('entry 側（view="entry"）に登録した一括アクションは module 側には出ない（view 分離）', () => {
      getDataviewBulkActionRegistry().add('entry', 'plugin-bulk-action', {
        id: 'plugin-bulk-action',
        label: 'entry 側専用',
      });

      const actions = setup([]);

      expect(actions.map((a) => a.id)).not.toContain('plugin-bulk-action');
      getDataviewBulkActionRegistry().remove('entry', 'plugin-bulk-action');
    });
  });

  describe('module.bulk-status.before / module.bulk-status.success', () => {
    afterEach(() => {
      const emit = window.ACMS.events.emit as ReturnType<typeof vi.fn>;
      emit.mockClear();
      emit.mockImplementation(async (_type: string, payload: unknown) => ({ canceled: false, event: payload }));
      vi.mocked(bulkChangeStatus).mockClear();
    });

    const callStatusOnAction = async (formData: FormData) => {
      const actions = setup([]);
      const action = actions.find((a) => a.id === 'status')!;
      await act(async () => {
        await (action.onAction as unknown as (formData: FormData, data: unknown[]) => Promise<void>)(formData, []);
      });
    };

    it('bulkChangeStatus の前後で module.bulk-status.before → module.bulk-status.success の順に発火する', async () => {
      const formData = new FormData();

      await callStatusOnAction(formData);

      const emit = window.ACMS.events.emit as ReturnType<typeof vi.fn>;
      expect(emit).toHaveBeenNthCalledWith(1, 'module.bulk-status.before', { formData });
      expect(bulkChangeStatus).toHaveBeenCalledWith(formData);
      expect(emit).toHaveBeenNthCalledWith(2, 'module.bulk-status.success', { formData });
    });

    it('module.bulk-status.before がキャンセルされた場合は bulkChangeStatus を呼ばない', async () => {
      (window.ACMS.events.emit as ReturnType<typeof vi.fn>).mockImplementation(async () => ({
        canceled: true,
        reason: 'blocked',
        event: {},
      }));

      await callStatusOnAction(new FormData());

      expect(bulkChangeStatus).not.toHaveBeenCalled();
    });
  });
});
