/** @vitest-environment jsdom */
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
import { render, screen, cleanup, fireEvent, waitFor } from '@testing-library/react';
import { DemoResultView } from './DemoResultView';
import { downloadImage } from '../imageUtils';
import { en } from '../i18n/en';

// The only import of imageUtils in this tree is the share fallback.
vi.mock('../imageUtils', () => ({ downloadImage: vi.fn() }));

const RESULT = 'data:image/png;base64,RESULT';
const GARMENT = 'data:image/png;base64,GARMENT';

type Props = React.ComponentProps<typeof DemoResultView>;

function renderResult(overrides: Partial<Props> = {}) {
  const props: Props = {
    resultImage: RESULT,
    originalClothingImage: GARMENT,
    productTitle: 'Amalfi Linen Dress',
    productUrl: '/product/amalfi-linen-dress',
    onTryAnother: vi.fn(),
    onClose: vi.fn(),
    t: en.result,
    shareT: en.share,
    ...overrides,
  };
  return { props, ...render(<DemoResultView {...props} />) };
}

beforeEach(() => {
  vi.mocked(downloadImage).mockClear();
});

afterEach(() => {
  cleanup();
  Reflect.deleteProperty(navigator, 'share');
  Reflect.deleteProperty(navigator, 'canShare');
});

// The detail the owner cared about most: judging fidelity without leaving the
// picture. It is the whole reason this view exists instead of the merchant one.
describe('the garment on the render', () => {
  it('sits on the result as a thumbnail', () => {
    const { container } = renderResult();
    const pip = container.querySelector('.aisthetix-tryon-figure-pip');
    expect(pip?.getAttribute('src')).toBe(GARMENT);
    expect(pip?.getAttribute('alt')).toBe(en.result.garmentThumbnail);
  });

  it('is simply absent when the garment image never arrived', () => {
    const { container } = renderResult({ originalClothingImage: undefined });
    expect(container.querySelector('.aisthetix-tryon-figure-image')?.getAttribute('src')).toBe(
      RESULT
    );
    expect(container.querySelector('.aisthetix-tryon-figure-pip')).toBeNull();
  });
});

describe('comparing against the original', () => {
  it('swaps to the side by side view and back', () => {
    const { container } = renderResult();

    fireEvent.click(screen.getByText(en.result.toggleCompare));
    expect(container.querySelectorAll('.aisthetix-compare-image')).toHaveLength(2);

    fireEvent.click(screen.getByText(en.result.toggleResult));
    expect(container.querySelector('.aisthetix-tryon-figure-pip')).toBeTruthy();
  });
});

describe('sharing the result', () => {
  it('hands the render to the share sheet as a file, with no upload', async () => {
    const share = vi.fn().mockResolvedValue(undefined);
    Object.defineProperty(navigator, 'share', { value: share, configurable: true });
    Object.defineProperty(navigator, 'canShare', { value: () => true, configurable: true });

    renderResult();
    fireEvent.click(screen.getByText(en.result.share));

    await waitFor(() => expect(share).toHaveBeenCalledTimes(1));
    const payload = share.mock.calls[0][0];
    expect(payload.files[0].type).toBe('image/png');
    expect(payload.files[0].name).toContain('amalfi-linen-dress');
    expect(downloadImage).not.toHaveBeenCalled();
  });

  // No share sheet means the file is saved instead, which is silent enough that
  // the shopper needs to be told it happened.
  it('saves the image and says so when there is no share sheet', async () => {
    renderResult();
    fireEvent.click(screen.getByText(en.result.share));

    await waitFor(() => expect(downloadImage).toHaveBeenCalledTimes(1));
    expect(screen.getByRole('status').textContent).toBe(en.share.downloaded);
  });
});

describe('the way onward', () => {
  it('offers a different photo and a way back to the store', () => {
    const { props } = renderResult();

    fireEvent.click(screen.getByText(en.result.tryDifferent));
    expect(props.onTryAnother).toHaveBeenCalledTimes(1);

    fireEvent.click(screen.getByText(en.result.continueShopping));
    expect(props.onClose).toHaveBeenCalledTimes(1);
  });

  it('names what was tried on', () => {
    renderResult();
    expect(screen.getByText(en.result.tryingOn('Amalfi Linen Dress'))).toBeTruthy();
  });
});
