import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import fs from 'fs/promises';
import path from 'path';
import os from 'os';
import { buildPromptWithImages } from '../src/prompt-builder';

describe('prompt-builder.ts', () => {
  let tempDir: string;

  beforeEach(async () => {
    tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'prompt-builder-test-'));
  });

  afterEach(async () => {
    try {
      await fs.rm(tempDir, { recursive: true, force: true });
    } catch (error) {
      // Ignore cleanup errors
    }
  });

  describe('buildPromptWithImages - no images', () => {
    it('should return plain text when no images provided', async () => {
      const basePrompt = 'Hello, world!';

      const result = await buildPromptWithImages(basePrompt);

      expect(result.content).toBe('Hello, world!');
      expect(result.hasImages).toBe(false);
    });

    it('should return plain text when empty image array provided', async () => {
      const basePrompt = 'Test prompt';

      const result = await buildPromptWithImages(basePrompt, []);

      expect(result.content).toBe('Test prompt');
      expect(result.hasImages).toBe(false);
    });

    it('should handle multiline prompts', async () => {
      const basePrompt = 'Line 1\nLine 2\nLine 3';

      const result = await buildPromptWithImages(basePrompt);

      expect(result.content).toBe('Line 1\nLine 2\nLine 3');
    });
  });

  describe('buildPromptWithImages - with images', () => {
    it('should build content blocks with single image', async () => {
      const imagePath = path.join(tempDir, 'test.png');
      await fs.writeFile(imagePath, Buffer.from('image data'));

      const result = await buildPromptWithImages('Check this image', [imagePath]);

      expect(result.hasImages).toBe(true);
      expect(Array.isArray(result.content)).toBe(true);

      const blocks = result.content as Array<{
        type: string;
        text?: string;
        source?: object;
      }>;
      expect(blocks).toHaveLength(2);
      expect(blocks[0]).toMatchObject({
        type: 'text',
        text: 'Check this image',
      });
      expect(blocks[1]).toMatchObject({
        type: 'image',
      });
    });

    it('should build content blocks with multiple images', async () => {
      const image1 = path.join(tempDir, 'img1.jpg');
      const image2 = path.join(tempDir, 'img2.png');

      await fs.writeFile(image1, Buffer.from('jpg data'));
      await fs.writeFile(image2, Buffer.from('png data'));

      const result = await buildPromptWithImages('Two images', [image1, image2]);

      expect(result.hasImages).toBe(true);

      const blocks = result.content as Array<{
        type: string;
        text?: string;
        source?: object;
      }>;
      expect(blocks).toHaveLength(3); // 1 text + 2 images
      expect(blocks[0].type).toBe('text');
      expect(blocks[1].type).toBe('image');
      expect(blocks[2].type).toBe('image');
    });

    it('should resolve relative paths with workDir', async () => {
      const imagePath = 'test.png';
      const fullPath = path.join(tempDir, imagePath);
      await fs.writeFile(fullPath, Buffer.from('data'));

      const result = await buildPromptWithImages('Test', [imagePath], tempDir);

      expect(result.hasImages).toBe(true);
      expect(Array.isArray(result.content)).toBe(true);
    });

    it('should handle absolute paths without workDir', async () => {
      const imagePath = path.join(tempDir, 'absolute.png');
      await fs.writeFile(imagePath, Buffer.from('data'));

      const result = await buildPromptWithImages('Test', [imagePath]);

      expect(result.hasImages).toBe(true);
    });
  });

  describe('buildPromptWithImages - includeImagePaths option', () => {
    it('should not include image paths by default', async () => {
      const imagePath = path.join(tempDir, 'test.png');
      await fs.writeFile(imagePath, Buffer.from('data'));

      const result = await buildPromptWithImages('Prompt', [imagePath]);

      const blocks = result.content as Array<{
        type: string;
        text?: string;
      }>;
      const textBlock = blocks.find((b) => b.type === 'text');

      expect(textBlock?.text).not.toContain('Attached images:');
      expect(textBlock?.text).toBe('Prompt');
    });

    it('should include image paths when requested', async () => {
      const imagePath = path.join(tempDir, 'test.png');
      await fs.writeFile(imagePath, Buffer.from('data'));

      const result = await buildPromptWithImages('Prompt', [imagePath], undefined, true);

      const blocks = result.content as Array<{
        type: string;
        text?: string;
      }>;
      const textBlock = blocks.find((b) => b.type === 'text');

      expect(textBlock?.text).toContain('Prompt');
      expect(textBlock?.text).toContain('Attached images:');
      expect(textBlock?.text).toContain(imagePath);
    });

    it('should format multiple image paths when included', async () => {
      const img1 = path.join(tempDir, 'img1.png');
      const img2 = path.join(tempDir, 'img2.jpg');

      await fs.writeFile(img1, Buffer.from('data1'));
      await fs.writeFile(img2, Buffer.from('data2'));

      const result = await buildPromptWithImages('Test', [img1, img2], undefined, true);

      const blocks = result.content as Array<{
        type: string;
        text?: string;
      }>;
      const textBlock = blocks.find((b) => b.type === 'text');

      expect(textBlock?.text).toContain('Attached images:');
      expect(textBlock?.text).toContain(img1);
      expect(textBlock?.text).toContain(img2);
    });
  });

  describe('buildPromptWithImages - edge cases', () => {
    it('should handle empty prompt with images', async () => {
      const imagePath = path.join(tempDir, 'test.png');
      await fs.writeFile(imagePath, Buffer.from('data'));

      const result = await buildPromptWithImages('', [imagePath]);

      expect(result.hasImages).toBe(true);

      const blocks = result.content as Array<{
        type: string;
        text?: string;
        source?: object;
      }>;
      // Should only have image block, no text block for empty string
      expect(blocks.length).toBeGreaterThan(0);
      expect(blocks.some((b) => b.type === 'image')).toBe(true);
    });

    it('should handle whitespace-only prompt with images', async () => {
      const imagePath = path.join(tempDir, 'test.png');
      await fs.writeFile(imagePath, Buffer.from('data'));

      const result = await buildPromptWithImages('   ', [imagePath]);

      expect(result.hasImages).toBe(true);

      const blocks = result.content as Array<{
        type: string;
        text?: string;
        source?: object;
      }>;
      // Whitespace-only is trimmed, so no text block should be added
      expect(blocks.every((b) => b.type !== 'text')).toBe(true);
    });

    it('should skip failed image loads', async () => {
      const validImage = path.join(tempDir, 'valid.png');
      const invalidImage = path.join(tempDir, 'nonexistent.png');

      await fs.writeFile(validImage, Buffer.from('data'));

      const result = await buildPromptWithImages('Test', [validImage, invalidImage]);

      expect(result.hasImages).toBe(true);

      const blocks = result.content as Array<{
        type: string;
        text?: string;
        source?: object;
      }>;
      const imageBlocks = blocks.filter((b) => b.type === 'image');

      // Only valid image should be included
      expect(imageBlocks).toHaveLength(1);
    });

    it('should handle mixed case in includeImagePaths parameter', async () => {
      const imagePath = path.join(tempDir, 'test.png');
      await fs.writeFile(imagePath, Buffer.from('data'));

      const resultFalse = await buildPromptWithImages('Test', [imagePath], undefined, false);
      const resultTrue = await buildPromptWithImages('Test', [imagePath], undefined, true);

      const blocksFalse = resultFalse.content as Array<{
        type: string;
        text?: string;
      }>;
      const blocksTrue = resultTrue.content as Array<{
        type: string;
        text?: string;
      }>;

      expect(blocksFalse[0].text).not.toContain('Attached images:');
      expect(blocksTrue[0].text).toContain('Attached images:');
    });
  });

  describe('buildPromptWithImages - content format', () => {
    it('should return string when only text and includeImagePaths false', async () => {
      const result = await buildPromptWithImages('Just text', undefined);

      expect(typeof result.content).toBe('string');
    });

    it('should return array when has images', async () => {
      const imagePath = path.join(tempDir, 'test.png');
      await fs.writeFile(imagePath, Buffer.from('data'));

      const result = await buildPromptWithImages('Text', [imagePath]);

      expect(Array.isArray(result.content)).toBe(true);
    });

    it('should preserve prompt formatting', async () => {
      const basePrompt = 'Line 1\n\nLine 2\n  Indented line';
      const imagePath = path.join(tempDir, 'test.png');
      await fs.writeFile(imagePath, Buffer.from('data'));

      const result = await buildPromptWithImages(basePrompt, [imagePath]);

      const blocks = result.content as Array<{
        type: string;
        text?: string;
      }>;
      const textBlock = blocks.find((b) => b.type === 'text');

      expect(textBlock?.text).toBe(basePrompt);
    });
  });
});
