/**
 * FlowBuilder Helper Functions Tests
 *
 * Tests utility functions for display names, icon mappings, app service info,
 * config summaries, step number calculation, and node layout.
 */
import { describe, it, expect } from 'vitest';
import { Edge } from '@xyflow/react';
import {
  getActionDisplayName,
  getAppNameFromIntegration,
  getAppServiceInfo,
  getConfigSummary,
  getActionIconName,
  getTriggerIconName,
  getTriggerDisplayName,
  calculateStepNumbers,
  autoLayoutNodes,
  deriveIntegrationFromType,
  getAppInfo,
  VERTICAL_SPACING,
  CENTER_X,
} from './flowBuilderHelpers';
import { WorkflowReactFlowNode } from '../types/reactflow';

describe('flowBuilderHelpers', () => {
  // ==========================================================================
  // getActionDisplayName Tests
  // ==========================================================================

  describe('getActionDisplayName', () => {
    it('returns display name for known action types', () => {
      expect(getActionDisplayName('wordpress/send_email')).toBe('Send Email');
      expect(getActionDisplayName('wordpress/create_update_post')).toBe('Create Update Post');
      expect(getActionDisplayName('wordpress/create_update_user')).toBe('Create Update User');
    });

    it('returns display name for Campaign Monitor actions', () => {
      expect(getActionDisplayName('campaignmonitor/add_subscriber')).toBe('Add Subscriber');
      expect(getActionDisplayName('campaignmonitor/remove_subscriber')).toBe('Remove Subscriber');
      expect(getActionDisplayName('campaignmonitor/update_subscriber')).toBe('Update Subscriber');
      expect(getActionDisplayName('campaignmonitor/add_subscriber_to_segment')).toBe(
        'Add Subscriber To Segment',
      );
      expect(getActionDisplayName('campaignmonitor/remove_subscriber_from_segment')).toBe(
        'Remove Subscriber From Segment',
      );
    });

    it('returns title-cased slug for unknown action types', () => {
      expect(getActionDisplayName('unknown_action')).toBe('Unknown Action');
      expect(getActionDisplayName('')).toBe('');
    });
  });

  // ==========================================================================
  // getAppNameFromIntegration Tests
  // ==========================================================================

  describe('getAppNameFromIntegration', () => {
    it('returns display name for known integrations', () => {
      expect(getAppNameFromIntegration('wordpress')).toBe('WordPress');
      expect(getAppNameFromIntegration('campaignmonitor')).toBe('Campaign Monitor');
      expect(getAppNameFromIntegration('mailchimp')).toBe('Mailchimp');
      expect(getAppNameFromIntegration('slack')).toBe('Slack');
      expect(getAppNameFromIntegration('hubspot')).toBe('HubSpot');
    });

    it('returns display name for additional integrations', () => {
      expect(getAppNameFromIntegration('kit')).toBe('Kit');
      expect(getAppNameFromIntegration('woocommerce')).toBe('WooCommerce');
      expect(getAppNameFromIntegration('airtable')).toBe('Airtable');
      expect(getAppNameFromIntegration('notion')).toBe('Notion');
      expect(getAppNameFromIntegration('gravity_forms')).toBe('Gravity Forms');
    });

    it('handles case-insensitively', () => {
      expect(getAppNameFromIntegration('WordPress')).toBe('WordPress');
      expect(getAppNameFromIntegration('MAILCHIMP')).toBe('Mailchimp');
    });

    it('capitalizes unknown integrations', () => {
      expect(getAppNameFromIntegration('custom')).toBe('Custom');
      expect(getAppNameFromIntegration('myintegration')).toBe('Myintegration');
    });
  });

  // ==========================================================================
  // getAppServiceInfo Tests
  // ==========================================================================

  describe('getAppServiceInfo', () => {
    it('returns WordPress for native action types', () => {
      expect(getAppServiceInfo('wordpress/send_email')).toEqual({
        appName: 'WordPress',
        appService: 'wordpress',
      });
      expect(getAppServiceInfo('wordpress/create_update_post')).toEqual({
        appName: 'WordPress',
        appService: 'wordpress',
      });
      expect(getAppServiceInfo('wordpress/create_update_user')).toEqual({
        appName: 'WordPress',
        appService: 'wordpress',
      });
    });

    it('returns WordPress for native trigger types', () => {
      expect(getAppServiceInfo('wordpress/user_registration')).toEqual({
        appName: 'WordPress',
        appService: 'wordpress',
      });
      expect(getAppServiceInfo('wordpress/post_published')).toEqual({
        appName: 'WordPress',
        appService: 'wordpress',
      });
      expect(getAppServiceInfo('wordpress/comment_submitted')).toEqual({
        appName: 'WordPress',
        appService: 'wordpress',
      });
    });

    it('returns external service info for namespaced action types', () => {
      expect(getAppServiceInfo('mailchimp/add_subscriber')).toEqual({
        appName: 'Mailchimp',
        appService: 'mailchimp',
      });
      expect(getAppServiceInfo('slack/send_message')).toEqual({
        appName: 'Slack',
        appService: 'slack',
      });
      expect(getAppServiceInfo('hubspot/create_contact')).toEqual({
        appName: 'HubSpot',
        appService: 'hubspot',
      });
    });

    it('returns WordPress as fallback for un-namespaced types', () => {
      expect(getAppServiceInfo('unknown_action')).toEqual({
        appName: 'WordPress',
        appService: 'wordpress',
      });
    });
  });

  // ==========================================================================
  // getConfigSummary Tests
  // ==========================================================================

  describe('getConfigSummary', () => {
    it('returns undefined for empty config', () => {
      expect(getConfigSummary({}, 'wordpress/send_email')).toBeUndefined();
      expect(getConfigSummary(null as any, 'wordpress/send_email')).toBeUndefined();
    });

    it('extracts email recipient for email actions', () => {
      expect(getConfigSummary({ to: 'user@example.com' }, 'wordpress/send_email')).toBe(
        'To: user@example.com',
      );
      expect(getConfigSummary({ email: 'user@example.com' }, 'mail_action')).toBe(
        'To: user@example.com',
      );
      expect(getConfigSummary({ recipient: 'user@example.com' }, 'wordpress/send_email')).toBe(
        'To: user@example.com',
      );
    });

    it('extracts status for update actions', () => {
      expect(getConfigSummary({ status: 'published' }, 'wordpress/update_post')).toBe(
        'Status → published',
      );
      expect(getConfigSummary({ new_status: 'active' }, 'change_status')).toBe('Status → active');
    });

    it('extracts list for subscriber actions', () => {
      expect(getConfigSummary({ list_id: 'list-123' }, 'add_subscriber')).toBe('List: list-123');
      expect(getConfigSummary({ list: 'Newsletter' }, 'subscriber_action')).toBe(
        'List: Newsletter',
      );
      expect(getConfigSummary({ email: 'user@test.com' }, 'create_contact')).toBe(
        'Email: user@test.com',
      );
    });

    it('extracts channel for message actions', () => {
      expect(getConfigSummary({ channel: '#general' }, 'send_message')).toBe('Channel: #general');
      expect(getConfigSummary({ channel: 'announcements' }, 'create_channel')).toBe(
        'Channel: announcements',
      );
    });

    it('extracts post type for post actions', () => {
      expect(getConfigSummary({ post_type: 'page' }, 'wordpress/create_post')).toBe('Type: page');
      expect(
        getConfigSummary(
          { title: 'A very long post title that should be truncated' },
          'wordpress/create_post',
        ),
      ).toBe('Title: A very long post title that sh...');
    });

    it('extracts role for user actions', () => {
      expect(getConfigSummary({ role: 'editor' }, 'wordpress/create_user')).toBe('Role: editor');
    });

    it('falls back to meaningful keys for other actions', () => {
      expect(getConfigSummary({ name: 'Test Name' }, 'generic_action')).toBe('name: Test Name');
      expect(getConfigSummary({ subject: 'Email Subject' }, 'generic_action')).toBe(
        'subject: Email Subject',
      );
    });

    it('truncates long values in fallback', () => {
      const longValue = 'This is a very long value that should definitely be truncated';
      expect(getConfigSummary({ name: longValue }, 'generic_action')).toBe(
        'name: This is a very long value...',
      );
    });

    it('returns undefined when no meaningful config found', () => {
      expect(getConfigSummary({ random_key: 'value' }, 'generic_action')).toBeUndefined();
      expect(getConfigSummary({ number_field: 123 }, 'generic_action')).toBeUndefined();
    });
  });

  // ==========================================================================
  // getActionIconName Tests
  // ==========================================================================

  describe('getActionIconName', () => {
    it('returns correct icons for known action types', () => {
      expect(getActionIconName('wordpress/send_email')).toBe('Mail');
      expect(getActionIconName('wordpress/create_post')).toBe('FilePlus');
      expect(getActionIconName('wordpress/update_post')).toBe('FilePenLine');
      expect(getActionIconName('wordpress/create_update_post')).toBe('FilePlus');
      expect(getActionIconName('wordpress/create_user')).toBe('UserPlus');
      expect(getActionIconName('wordpress/create_update_user')).toBe('UserPlus');
    });

    it('returns "Zap" as fallback for unknown action types', () => {
      expect(getActionIconName('unknown_action')).toBe('Zap');
      expect(getActionIconName('')).toBe('Zap');
    });
  });

  // ==========================================================================
  // getTriggerIconName Tests
  // ==========================================================================

  describe('getTriggerIconName', () => {
    it('returns correct icons for known trigger types', () => {
      expect(getTriggerIconName('wordpress/user_registration')).toBe('UserPlus');
      expect(getTriggerIconName('wordpress/user_profile_update')).toBe('Pencil');
      expect(getTriggerIconName('wordpress/user_role_change')).toBe('UserCog');
      expect(getTriggerIconName('wordpress/post_published')).toBe('FileCheck');
      expect(getTriggerIconName('wordpress/comment_submitted')).toBe('MessageSquare');
    });

    it('returns "Zap" as fallback for unknown trigger types', () => {
      expect(getTriggerIconName('unknown_trigger')).toBe('Zap');
      expect(getTriggerIconName('')).toBe('Zap');
    });
  });

  // ==========================================================================
  // getTriggerDisplayName Tests
  // ==========================================================================

  describe('getTriggerDisplayName', () => {
    it('returns display names for known trigger types', () => {
      expect(getTriggerDisplayName('wordpress/user_registration')).toBe('User Registration');
      expect(getTriggerDisplayName('wordpress/user_profile_update')).toBe('User Profile Update');
      expect(getTriggerDisplayName('wordpress/user_role_change')).toBe('User Role Change');
      expect(getTriggerDisplayName('wordpress/post_published')).toBe('Post Published');
      expect(getTriggerDisplayName('wordpress/comment_submitted')).toBe('Comment Submitted');
    });

    it('returns title-cased slug for unknown types', () => {
      expect(getTriggerDisplayName('unknown_trigger')).toBe('Unknown Trigger');
      expect(getTriggerDisplayName('')).toBe('');
    });
  });

  // ==========================================================================
  // calculateStepNumbers Tests
  // ==========================================================================

  describe('calculateStepNumbers', () => {
    it('returns empty map when no trigger node exists', () => {
      const nodes: WorkflowReactFlowNode[] = [
        { id: 'action-1', type: 'action', position: { x: 0, y: 0 }, data: {} as any },
      ];
      const edges: Edge[] = [];

      const result = calculateStepNumbers(nodes, edges);

      expect(result.size).toBe(0);
    });

    it('assigns step 1 to trigger node', () => {
      const nodes: WorkflowReactFlowNode[] = [
        { id: 'trigger-1', type: 'trigger', position: { x: 0, y: 0 }, data: {} as any },
      ];
      const edges: Edge[] = [];

      const result = calculateStepNumbers(nodes, edges);

      expect(result.get('trigger-1')).toBe(1);
    });

    it('assigns sequential step numbers based on edges', () => {
      const nodes: WorkflowReactFlowNode[] = [
        { id: 'trigger-1', type: 'trigger', position: { x: 0, y: 0 }, data: {} as any },
        { id: 'action-1', type: 'action', position: { x: 0, y: 100 }, data: {} as any },
        { id: 'action-2', type: 'action', position: { x: 0, y: 200 }, data: {} as any },
      ];
      const edges = [
        { id: 'e1', source: 'trigger-1', target: 'action-1' },
        { id: 'e2', source: 'action-1', target: 'action-2' },
      ];

      const result = calculateStepNumbers(nodes, edges);

      expect(result.get('trigger-1')).toBe(1);
      expect(result.get('action-1')).toBe(2);
      expect(result.get('action-2')).toBe(3);
    });

    it('handles branching edges', () => {
      const nodes: WorkflowReactFlowNode[] = [
        { id: 'trigger-1', type: 'trigger', position: { x: 0, y: 0 }, data: {} as any },
        { id: 'action-1', type: 'action', position: { x: 0, y: 100 }, data: {} as any },
        { id: 'action-2', type: 'action', position: { x: 100, y: 100 }, data: {} as any },
      ];
      const edges = [
        { id: 'e1', source: 'trigger-1', target: 'action-1' },
        { id: 'e2', source: 'trigger-1', target: 'action-2' },
      ];

      const result = calculateStepNumbers(nodes, edges);

      expect(result.get('trigger-1')).toBe(1);
      expect(result.get('action-1')).toBe(2);
      expect(result.get('action-2')).toBe(2);
    });

    it('assigns index-based step numbers to disconnected nodes', () => {
      const nodes: WorkflowReactFlowNode[] = [
        { id: 'trigger-1', type: 'trigger', position: { x: 0, y: 0 }, data: {} as any },
        { id: 'action-1', type: 'action', position: { x: 0, y: 100 }, data: {} as any },
        { id: 'disconnected', type: 'action', position: { x: 100, y: 100 }, data: {} as any },
      ];
      const edges = [{ id: 'e1', source: 'trigger-1', target: 'action-1' }];

      const result = calculateStepNumbers(nodes, edges);

      expect(result.get('trigger-1')).toBe(1);
      expect(result.get('action-1')).toBe(2);
      expect(result.get('disconnected')).toBe(3); // index + 1
    });
  });

  // ==========================================================================
  // autoLayoutNodes Tests
  // ==========================================================================

  describe('autoLayoutNodes', () => {
    it('positions trigger node first', () => {
      const nodes: WorkflowReactFlowNode[] = [
        { id: 'action-1', type: 'action', position: { x: 0, y: 0 }, data: {} as any },
        { id: 'trigger-1', type: 'trigger', position: { x: 0, y: 0 }, data: {} as any },
      ];

      const result = autoLayoutNodes(nodes);

      expect(result[0].id).toBe('trigger-1');
      expect(result[0].position.y).toBe(80); // START_Y
    });

    it('centers nodes horizontally at CENTER_X', () => {
      const nodes: WorkflowReactFlowNode[] = [
        { id: 'trigger-1', type: 'trigger', position: { x: 0, y: 0 }, data: {} as any },
        { id: 'action-1', type: 'action', position: { x: 100, y: 100 }, data: {} as any },
      ];

      const result = autoLayoutNodes(nodes);

      expect(result[0].position.x).toBe(CENTER_X);
      expect(result[1].position.x).toBe(CENTER_X);
    });

    it('spaces nodes vertically by VERTICAL_SPACING', () => {
      const nodes: WorkflowReactFlowNode[] = [
        { id: 'trigger-1', type: 'trigger', position: { x: 0, y: 0 }, data: {} as any },
        { id: 'action-1', type: 'action', position: { x: 0, y: 0 }, data: {} as any },
        { id: 'action-2', type: 'action', position: { x: 0, y: 0 }, data: {} as any },
      ];

      const result = autoLayoutNodes(nodes);

      expect(result[0].position.y).toBe(80);
      expect(result[1].position.y).toBe(80 + VERTICAL_SPACING);
      expect(result[2].position.y).toBe(80 + 2 * VERTICAL_SPACING);
    });

    it('sorts by stepNumber when available', () => {
      const nodes: WorkflowReactFlowNode[] = [
        {
          id: 'action-2',
          type: 'action',
          position: { x: 0, y: 0 },
          data: { stepNumber: 3 } as any,
        },
        {
          id: 'trigger-1',
          type: 'trigger',
          position: { x: 0, y: 0 },
          data: { stepNumber: 1 } as any,
        },
        {
          id: 'action-1',
          type: 'action',
          position: { x: 0, y: 0 },
          data: { stepNumber: 2 } as any,
        },
      ];

      const result = autoLayoutNodes(nodes);

      expect(result[0].id).toBe('trigger-1');
      expect(result[1].id).toBe('action-1');
      expect(result[2].id).toBe('action-2');
    });

    it('handles empty nodes array', () => {
      const result = autoLayoutNodes([]);
      expect(result).toEqual([]);
    });
  });

  // ==========================================================================
  // deriveIntegrationFromType Tests
  // ==========================================================================

  describe('deriveIntegrationFromType', () => {
    it('derives integration from namespace prefix', () => {
      expect(deriveIntegrationFromType('campaignmonitor/add_subscriber')).toBe('campaignmonitor');
      expect(deriveIntegrationFromType('woocommerce/create_order')).toBe('woocommerce');
      expect(deriveIntegrationFromType('wordpress/send_email')).toBe('wordpress');
      expect(deriveIntegrationFromType('mailchimp/add_subscriber')).toBe('mailchimp');
      expect(deriveIntegrationFromType('slack/send_message')).toBe('slack');
      expect(deriveIntegrationFromType('hubspot/create_contact')).toBe('hubspot');
      expect(deriveIntegrationFromType('kit/add_subscriber')).toBe('kit');
    });

    it('falls back to wordpress for un-namespaced types', () => {
      expect(deriveIntegrationFromType('send_email')).toBe('wordpress');
      expect(deriveIntegrationFromType('')).toBe('wordpress');
      expect(deriveIntegrationFromType('unknown_action')).toBe('wordpress');
      expect(deriveIntegrationFromType('custom_trigger')).toBe('wordpress');
    });
  });

  // ==========================================================================
  // getAppInfo Tests
  // ==========================================================================

  describe('getAppInfo', () => {
    it('uses saved integration when provided', () => {
      const result = getAppInfo('some_action', 'mailchimp');

      expect(result).toEqual({
        appName: 'Mailchimp',
        appService: 'mailchimp',
      });
    });

    it('derives integration from type namespace when no saved integration', () => {
      const result = getAppInfo('campaignmonitor/add_subscriber');

      expect(result).toEqual({
        appName: 'Campaign Monitor',
        appService: 'campaignmonitor',
      });
    });

    it('falls back to wordpress for un-namespaced types', () => {
      const result = getAppInfo('wordpress/send_email');

      expect(result).toEqual({
        appName: 'WordPress',
        appService: 'wordpress',
      });
    });

    it('prefers saved integration over derived', () => {
      const result = getAppInfo('campaignmonitor/add_subscriber', 'kit');

      expect(result).toEqual({
        appName: 'Kit',
        appService: 'kit',
      });
    });
  });

  // ==========================================================================
  // Constants Tests
  // ==========================================================================

  describe('constants', () => {
    it('exports layout constants', () => {
      expect(VERTICAL_SPACING).toBe(220);
      expect(CENTER_X).toBe(250);
    });
  });
});
