import { render } from '@testing-library/vue';
import { describe, expect, test } from 'vitest';
import UiFileUpload from '../UiFileUpload.vue';

describe('UiFileUpload', () => {
  test('renders drop label and choose button', () => {
    const { getByRole, getByText } = render(UiFileUpload, {
      props: {
        dropLabel: 'Drop files here',
        chooseLabel: 'Choose files',
      },
    });
    expect(getByText('Drop files here')).toBeInTheDocument();
    expect(getByRole('button', { name: 'Choose files' })).toBeInTheDocument();
  });

  test('emits dragenter and drop with DragEvent', async () => {
    const { getByRole, emitted } = render(UiFileUpload);
    const zone = getByRole('button', { name: 'File upload' });
    const file = new File(['x'], 'a.txt', { type: 'text/plain' });
    const files = {
      length: 1,
      item: (i: number) => (i === 0 ? file : null),
      *[Symbol.iterator]() {
        yield file;
      },
    } as unknown as FileList;
    const dataTransfer = { files } as unknown as DataTransfer;

    zone.dispatchEvent(
      new Event('dragenter', { bubbles: true, cancelable: true }),
    );
    expect(emitted('dragenter')).toHaveLength(1);

    const dropEv = new Event('drop', { bubbles: true, cancelable: true });
    Object.defineProperty(dropEv, 'dataTransfer', {
      value: dataTransfer,
      configurable: true,
    });
    zone.dispatchEvent(dropEv);
    expect(emitted('drop')).toHaveLength(1);
    expect(emitted('update:files')).toBeTruthy();
  });

  test('with multiple=false, drop emits only the first valid file', async () => {
    const { getByRole, emitted } = render(UiFileUpload, {
      props: {
        multiple: false,
        accept: '.txt,text/plain',
      },
    });
    const zone = getByRole('button', { name: 'File upload' });
    const first = new File(['a'], 'a.txt', { type: 'text/plain' });
    const second = new File(['b'], 'b.txt', { type: 'text/plain' });
    const list = [first, second];
    const files = {
      length: list.length,
      item: (i: number) => list[i] ?? null,
      *[Symbol.iterator]() {
        yield* list;
      },
    } as unknown as FileList;
    const dataTransfer = { files } as unknown as DataTransfer;
    const dropEv = new Event('drop', { bubbles: true, cancelable: true });
    Object.defineProperty(dropEv, 'dataTransfer', {
      value: dataTransfer,
      configurable: true,
    });
    zone.dispatchEvent(dropEv);
    expect(emitted('update:files')).toHaveLength(1);
    const payload = emitted('update:files')?.[0]?.[0] as File[];
    expect(payload).toHaveLength(1);
    expect(payload[0].name).toBe('a.txt');
  });

  test('footer shows custom maxSizeLabel when set', () => {
    const { getByText } = render(UiFileUpload, {
      props: {
        maxSizeLabel: 'Per file: 2 MB maximum',
        maxFileSizeBytes: 500 * 1024 * 1024,
      },
    });
    expect(getByText('Per file: 2 MB maximum')).toBeInTheDocument();
  });

  test('footer shows auto max size when maxSizeLabel is blank', () => {
    const { getByText } = render(UiFileUpload, {
      props: {
        maxSizeLabel: '',
        maxFileSizeBytes: 1024 * 1024,
      },
    });
    expect(getByText('Max size: 1.0 MB')).toBeInTheDocument();
  });

  test('when disabled, drop zone is not in tab order', () => {
    const { container } = render(UiFileUpload, {
      props: { disabled: true, ariaLabel: 'Attachment upload' },
    });
    const zone = container.querySelector(
      '[aria-label="Attachment upload"]',
    ) as HTMLElement;
    expect(zone).toBeTruthy();
    expect(zone.getAttribute('tabindex')).toBe('-1');
  });

  test('emits error for file failing accept', async () => {
    const { container, emitted } = render(UiFileUpload, {
      props: {
        accept: '.pdf',
      },
    });
    const input = container.querySelector(
      'input[type="file"]',
    ) as HTMLInputElement;
    const bad = new File(['x'], 'x.exe', { type: 'application/octet-stream' });
    const fileList = {
      length: 1,
      item: (i: number) => (i === 0 ? bad : null),
      *[Symbol.iterator]() {
        yield bad;
      },
    } as unknown as FileList;
    Object.defineProperty(input, 'files', {
      value: fileList,
      configurable: true,
    });
    input.dispatchEvent(new Event('change', { bubbles: true }));
    expect(emitted('error')).toBeTruthy();
    expect(emitted('error')?.[0]?.[0]).toMatchObject({
      message: 'Unsupported file type',
    });
  });
});
