import { describe, it, expect, vi, beforeEach } from 'vitest';
import { fetchClient } from '@ablogcms/fetch-client';
import { xhrUpload } from '@ablogcms/fetch-client/xhr-upload';
import { createAcmsMediaTransport } from './create-transport';

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

const okResponse = <T>(data: T) => ({ data, status: 200, statusText: 'OK', headers: new Headers() });

beforeEach(() => {
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  const w = window as any;
  w.ACMS = w.ACMS || {};
  w.ACMS.Config = { bid: '3' };
  w.ACMS.Library = {
    acmsLink: vi.fn((query: unknown) => `acmsLink(${JSON.stringify(query)})`),
  };
  w.$ = { ajax: vi.fn() };
  vi.mocked(fetchClient.get).mockReset();
  vi.mocked(xhrUpload).mockReset();
});

describe('createAcmsMediaTransport / fetchMediaList', () => {
  it('従来と同一の URL・クエリで ajax/edit/media.json を取得し、data をそのまま返す', async () => {
    vi.mocked(fetchClient.get).mockResolvedValue(okResponse({ items: [], pageAmount: 1 } as never));
    const transport = createAcmsMediaTransport();

    const setting = {
      page: 2,
      tag: 'tagA',
      keyword: 'kw',
      order: 'upload_date-desc',
      limit: 20,
      date: '',
      filetype: 'all' as const,
      fileext: 'all',
      year: '',
      month: '',
      owner: true,
      blogAxis: false,
    };
    const result = await transport.fetchMediaList(setting);

    expect(ACMS.Library.acmsLink).toHaveBeenCalledWith(
      {
        tpl: 'ajax/edit/media.json',
        bid: '3',
        page: 2,
        tag: 'tagA',
        keyword: 'kw',
        order: 'upload_date-desc',
        limit: 20,
        date: '',
        Query: {
          type: 'all',
          ext: 'all',
          year: '',
          month: '',
          owner: 'true',
          blogAxis: 'false',
          cache: expect.any(String),
        },
      },
      false
    );
    expect(fetchClient.get).toHaveBeenCalledWith(expect.stringContaining('ajax/edit/media.json'));
    expect(result).toEqual({ items: [], pageAmount: 1 });
  });
});

describe('createAcmsMediaTransport / fetchTagList', () => {
  it('cache パラメータ無しで ajax/edit/media-tag.json を取得する', async () => {
    vi.mocked(fetchClient.get).mockResolvedValue(okResponse(['tagA']));
    const transport = createAcmsMediaTransport();

    const result = await transport.fetchTagList();

    expect(ACMS.Library.acmsLink).toHaveBeenCalledWith({ bid: '3', tpl: 'ajax/edit/media-tag.json' }, false);
    expect(result).toEqual(['tagA']);
  });
});

describe('createAcmsMediaTransport / fetchMediaTags', () => {
  it('tags 省略時は tags クエリを付けずに取得する', async () => {
    vi.mocked(fetchClient.get).mockResolvedValue(okResponse(['tagA', 'tagB']));
    const transport = createAcmsMediaTransport();

    await transport.fetchMediaTags();

    expect(ACMS.Library.acmsLink).toHaveBeenCalledWith(
      { tpl: 'ajax/edit/media-tag.json', bid: '3', Query: { cache: expect.any(String) } },
      false
    );
    expect(fetchClient.get).toHaveBeenCalledWith(expect.any(String));
    expect(vi.mocked(fetchClient.get).mock.calls[0]).toHaveLength(1);
  });

  it('tags 指定時（空配列含む）は tags クエリを常に付けて取得する', async () => {
    vi.mocked(fetchClient.get).mockResolvedValue(okResponse([]));
    const transport = createAcmsMediaTransport();

    await transport.fetchMediaTags([]);

    expect(fetchClient.get).toHaveBeenCalledWith(expect.any(String), { params: { tags: '' } });

    await transport.fetchMediaTags(['a', 'b']);
    expect(fetchClient.get).toHaveBeenCalledWith(expect.any(String), { params: { tags: 'a,b' } });
  });
});

describe('createAcmsMediaTransport / fetchMediaDetail', () => {
  it('_mid パラメータ付きで ajax/edit/media-edit.json を取得する', async () => {
    vi.mocked(fetchClient.get).mockResolvedValue(okResponse({ status: 'success', item: { media_id: 9 } }));
    const transport = createAcmsMediaTransport();

    const result = await transport.fetchMediaDetail(9);

    expect(ACMS.Library.acmsLink).toHaveBeenCalledWith(
      { tpl: 'ajax/edit/media-edit.json', bid: '3', Query: { cache: expect.any(String) } },
      false
    );
    expect(fetchClient.get).toHaveBeenCalledWith(expect.any(String), { params: { _mid: 9 } });
    expect(result).toEqual({ status: 'success', item: { media_id: 9 } });
  });
});

describe('createAcmsMediaTransport / updateMedia・updateMediaAsNew', () => {
  it('updateMedia は _mid クエリのみで（bid なし）POST する', async () => {
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    (window as any).$.ajax.mockResolvedValue({ media_id: 1 });
    const transport = createAcmsMediaTransport();
    const formData = new FormData();

    await transport.updateMedia(1, formData);

    expect(ACMS.Library.acmsLink).toHaveBeenCalledWith({ Query: { _mid: 1 } });
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    expect((window as any).$.ajax).toHaveBeenCalledWith({
      url: expect.any(String),
      type: 'POST',
      data: formData,
      processData: false,
      contentType: false,
    });
  });

  it('updateMediaAsNew も同じ URL 構築規則で POST する', async () => {
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    (window as any).$.ajax.mockResolvedValue({ media_id: 2 });
    const transport = createAcmsMediaTransport();
    const formData = new FormData();

    await transport.updateMediaAsNew(2, formData);

    expect(ACMS.Library.acmsLink).toHaveBeenCalledWith({ Query: { _mid: 2 } });
  });
});

describe('createAcmsMediaTransport / deleteMedia・addTagsToMedia', () => {
  it('deleteMedia は bid クエリで POST する', async () => {
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    (window as any).$.ajax.mockResolvedValue(undefined);
    const transport = createAcmsMediaTransport();
    const formData = new FormData();

    await transport.deleteMedia(formData);

    expect(ACMS.Library.acmsLink).toHaveBeenCalledWith({ bid: '3' });
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    expect((window as any).$.ajax).toHaveBeenCalledWith({
      url: expect.any(String),
      type: 'POST',
      data: formData,
      processData: false,
      contentType: false,
    });
  });

  it('addTagsToMedia は bid クエリで POST する', async () => {
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    (window as any).$.ajax.mockResolvedValue(undefined);
    const transport = createAcmsMediaTransport();
    const formData = new FormData();

    await transport.addTagsToMedia(formData);

    expect(ACMS.Library.acmsLink).toHaveBeenCalledWith({ bid: '3' });
  });
});

describe('createAcmsMediaTransport / uploadMedia', () => {
  it('現在の location.href へ xhrUpload する', async () => {
    vi.mocked(xhrUpload).mockResolvedValue(okResponse({ media_id: 5 }) as never);
    const transport = createAcmsMediaTransport();
    const formData = new FormData();
    const onUploadProgress = vi.fn();

    const result = await transport.uploadMedia(formData, { onUploadProgress });

    expect(xhrUpload).toHaveBeenCalledWith(location.href, formData, { onUploadProgress });
    expect(result).toEqual(okResponse({ media_id: 5 }));
  });
});
