import { fireEvent, render, screen } from '@testing-library/react';
import { describe, expect, it, vi } from 'vitest';
import { ComposerAttachmentCard } from '@/pages/Chat/ComposerAttachmentCard';
import {
  applyProcessedImage,
  type ComposerAttachment,
} from '@/pages/Chat/composer-attachments';

function createAttachment(
  overrides: Partial<ComposerAttachment> = {},
): ComposerAttachment {
  return {
    id: 'attachment-1',
    displayName: 'photo.avif',
    source: 'user-upload',
    originalFile: new File(['original'], 'photo.avif', { type: 'image/avif' }),
    original: {
      blob: new Blob(['original'], { type: 'image/avif' }),
      mimeType: 'image/avif',
      bytes: 8_294_400,
      width: 3840,
      height: 2160,
      previewUrl: 'blob:original',
      transportFileName: 'photo.avif',
    },
    compressed: {
      blob: new Blob(['compressed'], { type: 'image/webp' }),
      mimeType: 'image/webp',
      bytes: 500_000,
      width: 1930,
      height: 1086,
      previewUrl: 'blob:compressed',
      transportFileName: 'photo.webp',
    },
    selectedVariant: 'compressed',
    status: 'compressed',
    ...overrides,
  };
}

describe('ComposerAttachmentCard', () => {
  it('shows both dimensions/bytes and the selected preview', () => {
    render(
      <ComposerAttachmentCard
        attachment={createAttachment()}
        modelRef="niancode-user-models/qwen3.7-plus"
        onSelectVariant={vi.fn()}
        onRemove={vi.fn()}
      />,
    );

    expect(screen.getByTestId('opencode-composer-image-preview'))
      .toHaveAttribute('src', 'blob:compressed');
    expect(screen.getByText(/3840\u00d72160/)).toBeInTheDocument();
    expect(screen.getByText(/1930\u00d71086/)).toBeInTheDocument();
    expect(screen.getByTestId('opencode-attachment-token-estimate'))
      .toHaveTextContent('\u7ea62049 Token');
    expect(screen.getByText('\u4f7f\u7528\u539f\u56fe')).toBeInTheDocument();
    expect(screen.getByText('\u4f7f\u7528\u538b\u7f29\u56fe')).toBeInTheDocument();
  });

  it('requests original/compressed selection without deleting the attachment', () => {
    const onSelectVariant = vi.fn();
    render(
      <ComposerAttachmentCard
        attachment={createAttachment()}
        modelRef="niancode-user-models/qwen3.7-plus"
        onSelectVariant={onSelectVariant}
        onRemove={vi.fn()}
      />,
    );

    fireEvent.click(screen.getByTestId('opencode-attachment-use-original'));
    fireEvent.click(screen.getByTestId('opencode-attachment-use-compressed'));

    expect(onSelectVariant).toHaveBeenNthCalledWith(1, 'original');
    expect(onSelectVariant).toHaveBeenNthCalledWith(2, 'compressed');
    expect(screen.getByTestId('opencode-composer-attachment')).toBeInTheDocument();
  });

  it('does not estimate a same-named Qwen model from another provider', () => {
    render(
      <ComposerAttachmentCard
        attachment={createAttachment()}
        modelRef="openai/qwen3.7-plus"
        onSelectVariant={vi.fn()}
        onRemove={vi.fn()}
      />,
    );

    expect(screen.queryByTestId('opencode-attachment-token-estimate'))
      .not.toBeInTheDocument();
  });

  it.each([
    ['failed', '\u56fe\u7247\u538b\u7f29\u5931\u8d25\uff0c\u53d1\u9001\u65f6\u5c06\u4f7f\u7528\u539f\u56fe\u3002'],
    ['skipped', 'GIF \u52a8\u753b\u4e0d\u4f1a\u81ea\u52a8\u538b\u7f29\uff0c\u53d1\u9001\u65f6\u5c06\u4f7f\u7528\u539f\u56fe\u3002'],
  ] as const)('shows %s warning without a meaningless toggle', (status, warning) => {
    render(
      <ComposerAttachmentCard
        attachment={createAttachment({
          status,
          warning,
          compressed: undefined,
          selectedVariant: 'original',
        })}
        modelRef="niancode-user-models/qwen3.7-plus"
        onSelectVariant={vi.fn()}
        onRemove={vi.fn()}
      />,
    );

    expect(screen.getByTestId('opencode-attachment-processing-status'))
      .toHaveTextContent(warning);
    expect(screen.queryByTestId('opencode-attachment-use-original'))
      .not.toBeInTheDocument();
    expect(screen.queryByTestId('opencode-attachment-use-compressed'))
      .not.toBeInTheDocument();
  });

  it('shows decoded original dimensions after later image processing fails', () => {
    const attachment = createAttachment({
      status: 'processing',
      compressed: undefined,
      selectedVariant: 'original',
      original: {
        ...createAttachment().original,
        width: undefined,
        height: undefined,
      },
    });
    const processed = applyProcessedImage(attachment, {
      status: 'failed',
      original: {
        blob: attachment.originalFile,
        mimeType: 'image/avif',
        bytes: attachment.originalFile.size,
        width: 3000,
        height: 2000,
      },
      defaultVariant: 'original',
      warning: '图片压缩失败，发送时将使用原图。',
    });

    render(
      <ComposerAttachmentCard
        attachment={processed}
        modelRef="niancode-user-models/qwen3.7-plus"
        onSelectVariant={vi.fn()}
        onRemove={vi.fn()}
      />,
    );

    expect(screen.getByText(/3000\u00d72000/)).toBeInTheDocument();
    expect(screen.getByTestId('opencode-attachment-processing-status'))
      .toHaveTextContent('图片压缩失败，发送时将使用原图。');
  });

  it('bounds a long status while retaining its complete accessible content', () => {
    const warning = `\u56fe\u7247\u5904\u7406\u4e2d${'\u5f88\u957f'.repeat(80)}`;
    render(
      <ComposerAttachmentCard
        attachment={createAttachment({
          status: 'processing',
          warning,
          compressed: undefined,
          selectedVariant: 'original',
        })}
        modelRef="niancode-user-models/qwen3.7-plus"
        onSelectVariant={vi.fn()}
        onRemove={vi.fn()}
      />,
    );

    const status = screen.getByTestId('opencode-attachment-processing-status');
    expect(status).toHaveTextContent(warning);
    expect(status).toHaveAttribute('title', warning);
    expect(status).toHaveAttribute('aria-label', warning);
    expect(status).toHaveClass('line-clamp-2', 'break-words');
  });
});
