import { describe, it, expect, vi, afterEach } from 'vitest';
import { waitFor, fireEvent } from '@testing-library/react';
import { mountRelatedEntryFromAttributes } from './mount-related-entry';
import type { RelatedEntryType } from './types';

vi.mock('./components/related-entries/related-entries', () => ({
  default: ({
    entries,
    type,
    title,
    moduleId,
    ctx,
    thumbnail,
    maxItem,
    onChange,
  }: {
    entries: RelatedEntryType[];
    type: string;
    title: string;
    moduleId: string;
    ctx: string;
    thumbnail?: string;
    maxItem: number;
    onChange: (entries: RelatedEntryType[]) => void;
  }) => (
    <div
      data-testid="related-entries-stub"
      data-type={type}
      data-title={title}
      data-module-id={moduleId}
      data-ctx={ctx}
      data-thumbnail={thumbnail}
      data-max-item={maxItem}
    >
      <span data-testid="entries">{JSON.stringify(entries)}</span>
      <button
        type="button"
        onClick={() => onChange([{ id: 99, title: 'new', categoryName: 'cat', image: '', url: '' }])}
      >
        change
      </button>
    </div>
  ),
}));

const sampleEntries: RelatedEntryType[] = [
  { id: 1, title: 'entry1', categoryName: 'cat1', image: 'img1', url: 'url1' },
];

describe('mountRelatedEntryFromAttributes', () => {
  afterEach(() => {
    document.body.innerHTML = '';
    vi.clearAllMocks();
  });

  it('json-id が無い場合はエラーを投げる', () => {
    const host = document.createElement('div');
    expect(() => mountRelatedEntryFromAttributes(host, {})).toThrow('Not Found json-id attribute.');
  });

  it('json-id が指す script の内容を entries として RelatedEntries をマウントする', async () => {
    document.body.innerHTML = `<script type="application/json" id="entries-json">${JSON.stringify(sampleEntries)}</script>`;
    const host = document.createElement('div');
    document.body.appendChild(host);

    const { entries, options } = mountRelatedEntryFromAttributes(host, {
      type: 'news',
      title: '関連ニュース',
      moduleId: '1',
      ctx: 'index',
      thumbnail: 'thumb',
      maxItem: '5',
      jsonId: 'entries-json',
    });

    expect(entries).toEqual(sampleEntries);
    expect(options).toEqual({
      type: 'news',
      title: '関連ニュース',
      moduleId: '1',
      ctx: 'index',
      thumbnail: 'thumb',
      maxItem: 5,
    });

    await waitFor(() => {
      expect(document.querySelector('[data-testid="related-entries-stub"]')).not.toBeNull();
    });

    const stub = document.querySelector('[data-testid="related-entries-stub"]')!;
    expect(stub.getAttribute('data-type')).toBe('news');
    expect(stub.getAttribute('data-title')).toBe('関連ニュース');
    expect(stub.getAttribute('data-module-id')).toBe('1');
    expect(stub.getAttribute('data-ctx')).toBe('index');
    expect(stub.getAttribute('data-thumbnail')).toBe('thumb');
    expect(stub.getAttribute('data-max-item')).toBe('5');
  });

  it('data-max-item を省略した場合は 0 になる', async () => {
    document.body.innerHTML = `<script type="application/json" id="entries-json">[]</script>`;
    const host = document.createElement('div');
    document.body.appendChild(host);

    mountRelatedEntryFromAttributes(host, { jsonId: 'entries-json' });

    await waitFor(() => {
      expect(document.querySelector('[data-testid="related-entries-stub"]')?.getAttribute('data-max-item')).toBe('0');
    });
  });

  it('変更されると host 自身に acms.related-entry.change が detail 付きで発火する', async () => {
    document.body.innerHTML = `<script type="application/json" id="entries-json">[]</script>`;
    const host = document.createElement('div');
    document.body.appendChild(host);
    const handleChange = vi.fn();
    host.addEventListener('acms.related-entry.change', handleChange);

    mountRelatedEntryFromAttributes(host, {
      type: 'news',
      title: '関連ニュース',
      moduleId: '1',
      ctx: 'index',
      maxItem: '5',
      jsonId: 'entries-json',
    });

    await waitFor(() => {
      expect(document.querySelector('[data-testid="related-entries-stub"]')).not.toBeNull();
    });

    fireEvent.click(document.querySelector('button')!);

    expect(handleChange).toHaveBeenCalledTimes(1);
    const event = handleChange.mock.calls[0][0] as CustomEvent;
    expect(event.detail).toEqual({
      entries: [{ id: 99, title: 'new', categoryName: 'cat', image: '', url: '' }],
      type: 'news',
      title: '関連ニュース',
      moduleId: '1',
      ctx: 'index',
      maxItem: 5,
    });
  });
});
