import { describe, expect, it } from 'vitest';
import {
  isRenderTool,
  generateFromToolCall,
  generateChart,
  generateTable,
  generateForm,
  generateCard,
  generateMetric,
  generateList,
  generateTabs,
} from '../../../src/main/a2ui/generator';
import type { A2UIServerMessage } from '../../../src/main/a2ui/types';

describe('A2UI generator', () => {
  describe('isRenderTool', () => {
    it('returns true for all render tools', () => {
      expect(isRenderTool('render_chart')).toBe(true);
      expect(isRenderTool('render_table')).toBe(true);
      expect(isRenderTool('render_form')).toBe(true);
      expect(isRenderTool('render_card')).toBe(true);
      expect(isRenderTool('render_metric')).toBe(true);
      expect(isRenderTool('render_list')).toBe(true);
      expect(isRenderTool('render_tabs')).toBe(true);
    });

    it('returns false for non-render tools', () => {
      expect(isRenderTool('search_posts')).toBe(false);
      expect(isRenderTool('get_post')).toBe(false);
      expect(isRenderTool('render_unknown')).toBe(false);
      expect(isRenderTool('')).toBe(false);
    });
  });

  describe('generateFromToolCall', () => {
    it('dispatches to chart generator', () => {
      const messages = generateFromToolCall('conv-1', 'render_chart', {
        chartType: 'bar',
        series: [{ label: 'A', value: 1 }],
      });

      expect(messages).not.toBeNull();
      expect(messages!.length).toBeGreaterThanOrEqual(2);
      expect(messages![0].type).toBe('createSurface');
    });

    it('returns null for unknown tool', () => {
      expect(generateFromToolCall('conv-1', 'search_posts', {})).toBeNull();
    });
  });

  describe('generateChart', () => {
    it('creates surface with chart component and data binding', () => {
      const messages = generateChart('conv-1', {
        chartType: 'bar',
        title: 'Sales',
        series: [
          { label: 'Jan', value: 10 },
          { label: 'Feb', value: 20 },
        ],
      });

      expect(messages).toHaveLength(3); // createSurface + updateComponents + updateDataModel

      const createMsg = messages[0] as Extract<A2UIServerMessage, { type: 'createSurface' }>;
      expect(createMsg.type).toBe('createSurface');
      expect(createMsg.conversationId).toBe('conv-1');

      const updateMsg = messages[1] as Extract<A2UIServerMessage, { type: 'updateComponents' }>;
      expect(updateMsg.type).toBe('updateComponents');
      expect(updateMsg.components).toHaveLength(1);
      expect(updateMsg.components[0].type).toBe('chart');
      expect(updateMsg.components[0].properties.chartType).toBe('bar');
      expect(updateMsg.components[0].dataBinding).toBe('/chartData');
      expect(updateMsg.rootIds).toHaveLength(1);

      const dataMsg = messages[2] as Extract<A2UIServerMessage, { type: 'updateDataModel' }>;
      expect(dataMsg.type).toBe('updateDataModel');
      expect(dataMsg.path).toBe('/chartData');
      expect(dataMsg.value).toEqual([
        { label: 'Jan', value: 10 },
        { label: 'Feb', value: 20 },
      ]);
    });

    it('creates stacked-bar chart with segment data', () => {
      const messages = generateChart('conv-1', {
        chartType: 'stacked-bar',
        title: 'Posts by Year',
        series: [
          {
            label: '2023',
            value: 30,
            segments: [
              { label: 'Published', value: 20 },
              { label: 'Draft', value: 10 },
            ],
          },
          {
            label: '2024',
            value: 45,
            segments: [
              { label: 'Published', value: 35 },
              { label: 'Draft', value: 10 },
            ],
          },
        ],
      });

      expect(messages).toHaveLength(3);

      const updateMsg = messages[1] as Extract<A2UIServerMessage, { type: 'updateComponents' }>;
      expect(updateMsg.components[0].properties.chartType).toBe('stacked-bar');

      const dataMsg = messages[2] as Extract<A2UIServerMessage, { type: 'updateDataModel' }>;
      expect(dataMsg.value).toEqual([
        {
          label: '2023',
          value: 30,
          segments: [
            { label: 'Published', value: 20 },
            { label: 'Draft', value: 10 },
          ],
        },
        {
          label: '2024',
          value: 45,
          segments: [
            { label: 'Published', value: 35 },
            { label: 'Draft', value: 10 },
          ],
        },
      ]);
    });
  });

  describe('generateTable', () => {
    it('creates surface with table component and row data', () => {
      const messages = generateTable('conv-1', {
        title: 'Posts',
        columns: ['Title', 'Status'],
        rows: [['Hello', 'published'], ['Draft', 'draft']],
      });

      expect(messages).toHaveLength(3);

      const updateMsg = messages[1] as Extract<A2UIServerMessage, { type: 'updateComponents' }>;
      expect(updateMsg.components[0].type).toBe('table');
      expect(updateMsg.components[0].properties.columns).toEqual(['Title', 'Status']);

      const dataMsg = messages[2] as Extract<A2UIServerMessage, { type: 'updateDataModel' }>;
      expect(dataMsg.path).toBe('/tableRows');
      expect(dataMsg.value).toEqual([['Hello', 'published'], ['Draft', 'draft']]);
    });
  });

  describe('generateCard', () => {
    it('creates surface with card component', () => {
      const messages = generateCard('conv-1', {
        title: 'My Card',
        body: 'Card body text',
        subtitle: 'Optional subtitle',
      });

      expect(messages).toHaveLength(2); // No data model for card

      const updateMsg = messages[1] as Extract<A2UIServerMessage, { type: 'updateComponents' }>;
      expect(updateMsg.components[0].type).toBe('card');
      expect(updateMsg.components[0].properties.title).toBe('My Card');
      expect(updateMsg.components[0].properties.body).toBe('Card body text');
    });

    it('includes card actions when provided', () => {
      const messages = generateCard('conv-1', {
        title: 'Action Card',
        body: 'Has actions',
        actions: [{ label: 'Open', action: 'openPost', payload: { postId: 'p1' } }],
      });

      const updateMsg = messages[1] as Extract<A2UIServerMessage, { type: 'updateComponents' }>;
      expect(updateMsg.components[0].actions).toEqual([
        { eventType: 'click', action: 'openPost', payload: { postId: 'p1' } },
      ]);
    });
  });

  describe('generateMetric', () => {
    it('creates surface with metric component', () => {
      const messages = generateMetric('conv-1', {
        label: 'Total Posts',
        value: '42',
      });

      expect(messages).toHaveLength(2);

      const updateMsg = messages[1] as Extract<A2UIServerMessage, { type: 'updateComponents' }>;
      expect(updateMsg.components[0].type).toBe('metric');
      expect(updateMsg.components[0].properties.label).toBe('Total Posts');
      expect(updateMsg.components[0].properties.value).toBe('42');
    });
  });

  describe('generateList', () => {
    it('creates surface with list component and item data', () => {
      const messages = generateList('conv-1', {
        title: 'Tags',
        items: ['react', 'typescript', 'electron'],
      });

      expect(messages).toHaveLength(3);

      const updateMsg = messages[1] as Extract<A2UIServerMessage, { type: 'updateComponents' }>;
      expect(updateMsg.components[0].type).toBe('list');

      const dataMsg = messages[2] as Extract<A2UIServerMessage, { type: 'updateDataModel' }>;
      expect(dataMsg.path).toBe('/listItems');
      expect(dataMsg.value).toEqual(['react', 'typescript', 'electron']);
    });
  });

  describe('generateForm', () => {
    it('creates surface with form, field components, and submit button', () => {
      const messages = generateForm('conv-1', {
        title: 'Edit Post',
        submitLabel: 'Save',
        fields: [
          { key: 'title', label: 'Title', inputType: 'text', defaultValue: 'Hello' },
          { key: 'draft', label: 'Draft', inputType: 'checkbox', defaultValue: true },
        ],
      });

      // createSurface + updateComponents + 2 updateDataModel (one per default value)
      expect(messages).toHaveLength(4);

      const updateMsg = messages[1] as Extract<A2UIServerMessage, { type: 'updateComponents' }>;
      // form + 2 fields + 1 submit button = 4
      expect(updateMsg.components).toHaveLength(4);

      const formComponent = updateMsg.components.find((c) => c.type === 'form');
      expect(formComponent).toBeDefined();
      expect(formComponent!.children).toHaveLength(3); // 2 fields + submit button

      const textField = updateMsg.components.find((c) => c.type === 'textField');
      expect(textField).toBeDefined();
      expect(textField!.dataBinding).toBe('/formData/title');

      const checkBox = updateMsg.components.find((c) => c.type === 'checkBox');
      expect(checkBox).toBeDefined();
      expect(checkBox!.dataBinding).toBe('/formData/draft');

      const submitButton = updateMsg.components.find((c) => c.type === 'button');
      expect(submitButton).toBeDefined();
      expect(submitButton!.properties.label).toBe('Save');
    });

    it('maps select inputType to choicePicker', () => {
      const messages = generateForm('conv-1', {
        submitLabel: 'Go',
        fields: [
          {
            key: 'status',
            label: 'Status',
            inputType: 'select',
            options: [
              { label: 'Draft', value: 'draft' },
              { label: 'Published', value: 'published' },
            ],
          },
        ],
      });

      const updateMsg = messages[1] as Extract<A2UIServerMessage, { type: 'updateComponents' }>;
      const picker = updateMsg.components.find((c) => c.type === 'choicePicker');
      expect(picker).toBeDefined();
    });

    it('maps date inputType to dateTimeInput', () => {
      const messages = generateForm('conv-1', {
        submitLabel: 'Set',
        fields: [{ key: 'date', label: 'Date', inputType: 'date' }],
      });

      const updateMsg = messages[1] as Extract<A2UIServerMessage, { type: 'updateComponents' }>;
      const dateInput = updateMsg.components.find((c) => c.type === 'dateTimeInput');
      expect(dateInput).toBeDefined();
    });
  });

  describe('generateTabs', () => {
    it('creates surface with tabs and child components', () => {
      const messages = generateTabs('conv-1', {
        tabs: [
          {
            label: 'Overview',
            content: [{ type: 'text', text: 'Tab content' }],
          },
          {
            label: 'Details',
            content: [{ type: 'metric', label: 'Count', value: '5' }],
          },
        ],
      });

      expect(messages).toHaveLength(2); // createSurface + updateComponents

      const updateMsg = messages[1] as Extract<A2UIServerMessage, { type: 'updateComponents' }>;
      const tabsComponent = updateMsg.components.find((c) => c.type === 'tabs');
      expect(tabsComponent).toBeDefined();
      expect(tabsComponent!.children).toHaveLength(2);
      expect(tabsComponent!.properties.tabLabels).toEqual(['Overview', 'Details']);
    });

    it('creates chart components inside tabs with series in properties', () => {
      const messages = generateTabs('conv-1', {
        tabs: [
          {
            label: 'Stats',
            content: [{
              type: 'chart',
              chartType: 'bar',
              title: 'Monthly Posts',
              series: [{ label: 'Jan', value: 5 }, { label: 'Feb', value: 8 }],
            }],
          },
        ],
      });

      expect(messages).toHaveLength(2);
      const updateMsg = messages[1] as Extract<A2UIServerMessage, { type: 'updateComponents' }>;

      const chartComponent = updateMsg.components.find((c) => c.type === 'chart');
      expect(chartComponent).toBeDefined();
      expect(chartComponent!.properties.chartType).toBe('bar');
      expect(chartComponent!.properties.title).toBe('Monthly Posts');
      expect(chartComponent!.properties.series).toEqual([
        { label: 'Jan', value: 5 },
        { label: 'Feb', value: 8 },
      ]);
    });

    it('creates table components inside tabs with rows in properties', () => {
      const messages = generateTabs('conv-1', {
        tabs: [
          {
            label: 'Data',
            content: [{
              type: 'table',
              title: 'Recent Posts',
              columns: ['Title', 'Status'],
              rows: [['Hello', 'published'], ['Draft', 'draft']],
            }],
          },
        ],
      });

      expect(messages).toHaveLength(2);
      const updateMsg = messages[1] as Extract<A2UIServerMessage, { type: 'updateComponents' }>;

      const tableComponent = updateMsg.components.find((c) => c.type === 'table');
      expect(tableComponent).toBeDefined();
      expect(tableComponent!.properties.columns).toEqual(['Title', 'Status']);
      expect(tableComponent!.properties.rows).toEqual([
        ['Hello', 'published'],
        ['Draft', 'draft'],
      ]);
    });
  });
});
