import { describe, it, beforeEach } from '@jest/globals';
import { screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { renderWithProviders } from '../.././/./../../test-utils/test-utils';
import TriggerNode, { TriggerReduce, TriggerConnects, TriggerNodeCanConnect, type TriggerNodeData } from '../../../../../src/pages/VisProgPage/visualProgrammingUI/nodes/TriggerNode';
import useFlowStore from '../../../../../src/pages/VisProgPage/visualProgrammingUI/VisProgStores';
import type { Node } from '@xyflow/react';
import '@testing-library/jest-dom';

describe('TriggerNode', () => {
  let user: ReturnType<typeof userEvent.setup>;

  beforeEach(() => {
    user = userEvent.setup();
  });

  describe('Rendering', () => {
    it('should render TriggerNode with keywords type', () => {
      const mockNode: Node<TriggerNodeData> = {
        id: 'trigger-1',
        type: 'trigger',
        position: { x: 0, y: 0 },
        data: {
          label: 'Keyword Trigger',
          droppable: true,
          triggerType: 'keywords',
          triggers: [],
          hasReduce: true,
        },
      };

      renderWithProviders(
        <TriggerNode
          id={mockNode.id}
          type={mockNode.type as string}
          data={mockNode.data as any}
          selected={false}
          isConnectable={true}
          zIndex={0}
          dragging={false}
          selectable={true}
          deletable={true}
          draggable={true}
          positionAbsoluteX={0}
          positionAbsoluteY={0}
        />
      );

      expect(screen.getByText(/Triggers when the keyword is spoken/i)).toBeInTheDocument();
      expect(screen.getByPlaceholderText('...')).toBeInTheDocument();
    });

    it('should render TriggerNode with emotion type', () => {
      const mockNode: Node<TriggerNodeData> = {
        id: 'trigger-2',
        type: 'trigger',
        position: { x: 0, y: 0 },
        data: {
          label: 'Emotion Trigger',
          droppable: true,
          triggerType: 'emotion',
          triggers: [],
          hasReduce: true,
        },
      };

      renderWithProviders(
        <TriggerNode
          id={mockNode.id}
          type={mockNode.type as string}
          data={mockNode.data as any}
          selected={false}
          isConnectable={true}
          zIndex={0}
          dragging={false}
          selectable={true}
          deletable={true}
          draggable={true}
          positionAbsoluteX={0}
          positionAbsoluteY={0}
        />
      );

      expect(screen.getByText(/Emotion\?/i)).toBeInTheDocument();
    });
  });

  describe('User Interactions', () => {
    it('should add a new keyword', async () => {
      const mockNode: Node<TriggerNodeData> = {
        id: 'trigger-1',
        type: 'trigger',
        position: { x: 0, y: 0 },
        data: {
          label: 'Keyword Trigger',
          droppable: true,
          triggerType: 'keywords',
          triggers: [],
          hasReduce: true,
        },
      };

      useFlowStore.setState({ nodes: [mockNode], edges: [] });

      renderWithProviders(
        <TriggerNode
          id={mockNode.id}
          type={mockNode.type as string}
          data={mockNode.data as any}
          selected={false}
          isConnectable={true}
          zIndex={0}
          dragging={false}
          selectable={true}
          deletable={true}
          draggable={true}
          positionAbsoluteX={0}
          positionAbsoluteY={0}
        />
      );

      const input = screen.getByPlaceholderText('...');
      await user.type(input, 'hello{enter}');

      await waitFor(() => {
        const node = useFlowStore.getState().nodes.find(n => n.id === 'trigger-1') as Node<TriggerNodeData> | undefined;
        expect(node?.data.triggers.length).toBe(1);
        expect(node?.data.triggers[0].keyword).toBe('hello');
      });

    });

    it('should remove a keyword when cleared', async () => {
      const mockNode: Node<TriggerNodeData> = {
        id: 'trigger-1',
        type: 'trigger',
        position: { x: 0, y: 0 },
        data: {
          label: 'Keyword Trigger',
          droppable: true,
          triggerType: 'keywords',
          triggers: [{ id: 'kw1', keyword: 'hello' }],
          hasReduce: true,
        },
      };

      useFlowStore.setState({ nodes: [mockNode], edges: [] });

      renderWithProviders(
        <TriggerNode
          id={mockNode.id}
          type={mockNode.type as string}
          data={mockNode.data as any}
          selected={false}
          isConnectable={true}
          zIndex={0}
          dragging={false}
          selectable={true}
          deletable={true}
          draggable={true}
          positionAbsoluteX={0}
          positionAbsoluteY={0}
        />
      );

      const input = screen.getByDisplayValue('hello');
      for (let i = 0; i < 'hello'.length; i++) {
        await user.type(input, '{backspace}');
      }
      await user.type(input, '{enter}');

      await waitFor(() => {
        const node = useFlowStore.getState().nodes.find(n => n.id === 'trigger-1') as Node<TriggerNodeData> | undefined;
        expect(node?.data.triggers.length).toBe(0);
        });

    });
  });

  describe('TriggerReduce Function', () => {
    it('should reduce a trigger node to its essential data', () => {
      const triggerNode: Node = {
        id: 'trigger-1',
        type: 'trigger',
        position: { x: 0, y: 0 },
        data: {
          label: 'Keyword Trigger',
          droppable: true,
          triggerType: 'keywords',
          triggers: [{ id: 'kw1', keyword: 'hello' }],
          hasReduce: true,
        },
      };

    const allNodes: Node[] = [triggerNode];
    const result = TriggerReduce(triggerNode, allNodes);

    expect(result).toEqual({
      id: 'trigger-1',
      type: 'keywords',
      label: 'Keyword Trigger',
      keywords: [{ id: 'kw1', keyword: 'hello' }],
    });
    });
  });


  describe('TriggerConnects Function', () => {
    it('should handle connection without errors', () => {
      const node1: Node = {
        id: 'trigger-1',
        type: 'trigger',
        position: { x: 0, y: 0 },
        data: {
          label: 'Trigger 1',
          droppable: true,
          triggerType: 'keywords',
          triggers: [],
          hasReduce: true,
        },
      };

      const node2: Node = {
        id: 'norm-1',
        type: 'norm',
        position: { x: 100, y: 0 },
        data: {
          label: 'Norm 1',
          droppable: true,
          norm: 'test',
          hasReduce: true,
        },
      };

      expect(() => {
        TriggerConnects(node1, node2, true);
        TriggerConnects(node1, node2, false);
      }).not.toThrow();
    });

    it('should return true for TriggerNodeCanConnect if connection exists', () => {
      const connection = { source: 'trigger-1', target: 'norm-1' };
      expect(TriggerNodeCanConnect(connection as any)).toBe(true);
    });
  });
});
