import { describe, it, expect } from 'vitest';
import type {
  Message,
} from './types';
import {
  generateMessageId,
  generateContextId,
  generateTaskId,
  createTextMessage,
  createTaskStatus,
  createArtifact,
  serializeMessageToXml,
} from './helpers';

describe('A2A Types', () => {
  describe('createTextMessage', () => {
    it('produces conformant Message with required fields', () => {
      const msg = createTextMessage('sender-1', 'Hello');
      expect(msg.messageId).toBeTruthy();
      expect(typeof msg.messageId).toBe('string');
      expect(msg.role).toBe('ROLE_USER');
      expect(msg.parts).toHaveLength(1);
      expect(msg.parts[0].text).toBe('Hello');
      expect(msg.parts[0].mediaType).toBe('text/plain');
      expect(msg.metadata).toBeDefined();
      expect(msg.metadata?.fromId).toBe('sender-1');
    });

    it('includes contextId when provided', () => {
      const msg = createTextMessage('sender-1', 'Hello', { contextId: 'ctx-123' });
      expect(msg.contextId).toBe('ctx-123');
    });

    it('includes hopCount in metadata defaulting to 0', () => {
      const msg = createTextMessage('sender-1', 'Hello');
      expect(msg.metadata?.hopCount).toBe(0);
    });
  });

  describe('serializeMessageToXml', () => {
    it('produces valid XML envelope', () => {
      const msg = createTextMessage('agent-1', 'Test content', {
        contextId: 'ctx-42',
        fromName: 'Agent One',
      });
      const xml = serializeMessageToXml(msg);
      expect(xml).toContain('<agent-message');
      expect(xml).toContain('from-id="agent-1"');
      expect(xml).toContain('from-name="Agent One"');
      expect(xml).toContain(`message-id="${msg.messageId}"`);
      expect(xml).toContain('context-id="ctx-42"');
      expect(xml).toContain('hop-count="0"');
      expect(xml).toContain('<content>Test content</content>');
    });

    it('escapes special characters in content', () => {
      const msg = createTextMessage('s', 'a < b > c & d "e"');
      const xml = serializeMessageToXml(msg);
      expect(xml).toContain('a &lt; b &gt; c &amp; d &quot;e&quot;');
    });

    it('handles message without contextId', () => {
      const msg = createTextMessage('s', 'hi');
      delete msg.contextId;
      const xml = serializeMessageToXml(msg);
      expect(xml).toContain('context-id=""');
      expect(xml).not.toContain('undefined');
    });
  });

  describe('generateMessageId', () => {
    it('produces unique IDs', () => {
      const id1 = generateMessageId();
      const id2 = generateMessageId();
      expect(id1).not.toBe(id2);
    });
  });

  describe('generateContextId', () => {
    it('produces unique IDs with ctx- prefix', () => {
      const id = generateContextId();
      expect(id.startsWith('ctx-')).toBe(true);
    });
  });

  describe('generateTaskId', () => {
    it('format — task- prefix and uniqueness', () => {
      const id1 = generateTaskId();
      const id2 = generateTaskId();
      expect(id1.startsWith('task-')).toBe(true);
      expect(id1).not.toBe(id2);
    });
  });

  describe('createTaskStatus', () => {
    it('sets state correctly', () => {
      const status = createTaskStatus('TASK_STATE_WORKING');
      expect(status.state).toBe('TASK_STATE_WORKING');
    });

    it('includes ISO timestamp', () => {
      const status = createTaskStatus('TASK_STATE_SUBMITTED');
      expect(status.timestamp).toBeDefined();
      if (!status.timestamp) throw new Error('expected timestamp');
      const ts = status.timestamp;
      expect(() => new Date(ts)).not.toThrow();
      expect(new Date(ts).toISOString()).toBe(ts);
    });

    it('attaches optional message as full Message object', () => {
      const msg: Message = {
        messageId: 'msg-1',
        role: 'ROLE_AGENT',
        parts: [{ text: 'done' }],
      };
      const status = createTaskStatus('TASK_STATE_COMPLETED', msg);
      expect(status.message).toBe(msg);
      expect(status.message?.messageId).toBe('msg-1');
      expect(status.message?.parts[0].text).toBe('done');
    });
  });

  describe('createArtifact', () => {
    it('creates valid Artifact with parts', () => {
      const artifact = createArtifact('report', 'Hello world');
      expect(artifact.name).toBe('report');
      expect(artifact.parts).toHaveLength(1);
      expect(artifact.parts[0].text).toBe('Hello world');
    });

    it('generates unique artifactId', () => {
      const a1 = createArtifact('a', 'x');
      const a2 = createArtifact('b', 'y');
      expect(a1.artifactId).not.toBe(a2.artifactId);
      expect(a1.artifactId.startsWith('artifact-')).toBe(true);
    });

    it('sets mediaType text/plain for text', () => {
      const artifact = createArtifact('doc', 'content');
      expect(artifact.parts[0].mediaType).toBe('text/plain');
    });

  });
});
