import { describe, it, expect, vi, beforeAll } from 'vitest';
import type { Editor } from '@tiptap/core';
import type { MediaItem } from '@features/media/types';
import { selectInsertableMedia, insertMediaBlocks } from './insertMediaBlocks';

const createMediaItem = (overrides: Partial<MediaItem> = {}): MediaItem =>
  ({
    media_id: 1,
    media_type: 'image',
    media_thumbnail: 'thumb.jpg',
    media_root_path: 'media/1.jpg',
    media_size: '100x100',
    media_permalink: 'media/1',
    media_icon: 'icon.png',
    ...overrides,
  }) as MediaItem;

beforeAll(() => {
  const target = window as unknown as { ACMS?: Record<string, unknown> };
  target.ACMS = { ...target.ACMS, Config: { eid: '1' } };
});

type ChainMock = {
  insertContentAt: ReturnType<typeof vi.fn>;
  focus: ReturnType<typeof vi.fn>;
  run: ReturnType<typeof vi.fn>;
};

const createEditorMock = (): { editor: Editor; chain: ChainMock } => {
  const chain = {} as ChainMock;
  chain.insertContentAt = vi.fn(() => chain);
  chain.focus = vi.fn(() => chain);
  chain.run = vi.fn(() => true);
  const editor = {
    chain: vi.fn(() => chain),
    extensionManager: {
      extensions: [{ name: 'imageBlock', options: { getImageDefaultLightbox: () => true } }],
    },
  } as unknown as Editor;
  return { editor, chain };
};

describe('selectInsertableMedia', () => {
  describe("accept='image' (画像ブロックの枠)", () => {
    it('画像はサムネイルを持つものだけを選択順を保って返す', () => {
      const a = createMediaItem({ media_id: 1, media_type: 'image', media_thumbnail: 'a.jpg' });
      const b = createMediaItem({ media_id: 2, media_type: 'image', media_thumbnail: '' });
      const c = createMediaItem({ media_id: 3, media_type: 'image', media_thumbnail: 'c.jpg' });
      expect(selectInsertableMedia([a, b, c], 'image')).toEqual([a, c]);
    });

    it('svg もサムネイルを持つものだけ対象にする', () => {
      const svg = createMediaItem({ media_type: 'svg', media_thumbnail: 'a.svg' });
      const noThumb = createMediaItem({ media_type: 'svg', media_thumbnail: '' });
      expect(selectInsertableMedia([svg, noThumb], 'image')).toEqual([svg]);
    });

    it('ファイルは画像枠では除外する', () => {
      const img = createMediaItem({ media_id: 1, media_type: 'image', media_thumbnail: 'a.jpg' });
      const file = createMediaItem({ media_id: 2, media_type: 'file', media_thumbnail: '' });
      expect(selectInsertableMedia([img, file], 'image')).toEqual([img]);
    });
  });

  describe("accept='file' (ファイルブロックの枠)", () => {
    it('画像以外のファイルだけを選択順を保って返す', () => {
      const file1 = createMediaItem({ media_id: 1, media_type: 'file', media_thumbnail: '' });
      const file2 = createMediaItem({ media_id: 2, media_type: 'file', media_thumbnail: 'b.jpg' });
      expect(selectInsertableMedia([file1, file2], 'file')).toEqual([file1, file2]);
    });

    it('画像・svg はファイル枠では除外する', () => {
      const file = createMediaItem({ media_id: 1, media_type: 'file' });
      const img = createMediaItem({ media_id: 2, media_type: 'image', media_thumbnail: 'a.jpg' });
      const svg = createMediaItem({ media_id: 3, media_type: 'svg', media_thumbnail: 's.svg' });
      expect(selectInsertableMedia([file, img, svg], 'file')).toEqual([file]);
    });
  });

  describe("accept='all' (エディタ本文への直接ドロップ)", () => {
    it('画像はサムネイル必須・ファイルは全件で混在を順序通り返す', () => {
      const img1 = createMediaItem({ media_id: 1, media_type: 'image', media_thumbnail: 'a.jpg' });
      const file1 = createMediaItem({ media_id: 2, media_type: 'file', media_thumbnail: '' });
      const imgNoThumb = createMediaItem({ media_id: 3, media_type: 'image', media_thumbnail: '' });
      const file2 = createMediaItem({ media_id: 4, media_type: 'file', media_thumbnail: '' });
      expect(selectInsertableMedia([img1, file1, imgNoThumb, file2], 'all')).toEqual([img1, file1, file2]);
    });
  });

  it('空配列を渡すと空配列を返す', () => {
    expect(selectInsertableMedia([], 'image')).toEqual([]);
    expect(selectInsertableMedia([], 'file')).toEqual([]);
    expect(selectInsertableMedia([], 'all')).toEqual([]);
  });
});

describe('insertMediaBlocks', () => {
  const range = { from: 5, to: 8 };

  it("accept='all' は画像・ファイル混在を media_type で振り分け、1回の insertContentAt で順序通り一括挿入する", () => {
    const { editor, chain } = createEditorMock();
    const img = createMediaItem({ media_id: 1, media_type: 'image', media_thumbnail: 'a.jpg' });
    const file = createMediaItem({ media_id: 2, media_type: 'file', media_thumbnail: '' });

    const inserted = insertMediaBlocks(editor, [img, file], 'all', 'cls', range);

    expect(inserted).toBe(2);
    expect(chain.insertContentAt).toHaveBeenCalledTimes(1);
    expect(chain.insertContentAt).toHaveBeenCalledWith(range, [
      expect.objectContaining({ type: 'imageBlock' }),
      expect.objectContaining({ type: 'fileBlock' }),
    ]);
    expect(chain.focus).toHaveBeenCalledTimes(1);
    expect(chain.run).toHaveBeenCalledTimes(1);
  });

  it("accept='image' の枠ではファイルを挿入せず、画像のみ挿入する", () => {
    const { editor, chain } = createEditorMock();
    const img = createMediaItem({ media_id: 1, media_type: 'image', media_thumbnail: 'a.jpg' });
    const file = createMediaItem({ media_id: 2, media_type: 'file', media_thumbnail: '' });

    const inserted = insertMediaBlocks(editor, [img, file], 'image', '', range);

    expect(inserted).toBe(1);
    const contents = chain.insertContentAt.mock.calls[0][1] as Array<{ type: string }>;
    expect(contents).toEqual([expect.objectContaining({ type: 'imageBlock' })]);
  });

  it("accept='file' の枠では画像を挿入せず、ファイルのみ挿入する", () => {
    const { editor, chain } = createEditorMock();
    const img = createMediaItem({ media_id: 1, media_type: 'image', media_thumbnail: 'a.jpg' });
    const file = createMediaItem({ media_id: 2, media_type: 'file', media_thumbnail: '' });

    const inserted = insertMediaBlocks(editor, [img, file], 'file', '', range);

    expect(inserted).toBe(1);
    const contents = chain.insertContentAt.mock.calls[0][1] as Array<{ type: string }>;
    expect(contents).toEqual([expect.objectContaining({ type: 'fileBlock' })]);
  });

  it('imageBlock には editor の設定から決めた noLightbox を付与する', () => {
    const { editor, chain } = createEditorMock();
    const img = createMediaItem({ media_type: 'image', media_thumbnail: 'a.jpg' });

    insertMediaBlocks(editor, [img], 'image', '', range);

    // getImageDefaultLightbox() === true なので noLightbox は 'false'
    const contents = chain.insertContentAt.mock.calls[0][1] as Array<{ type: string; attrs: Record<string, unknown> }>;
    expect(contents[0].attrs.noLightbox).toBe('false');
  });

  it('挿入対象が無いときは chain を組み立てず 0 を返す', () => {
    const { editor, chain } = createEditorMock();
    const noThumb = createMediaItem({ media_type: 'image', media_thumbnail: '' });

    const inserted = insertMediaBlocks(editor, [noThumb], 'image', '', range);

    expect(inserted).toBe(0);
    expect(editor.chain).not.toHaveBeenCalled();
    expect(chain.run).not.toHaveBeenCalled();
  });
});
