/**
 * Execution Processor Service Tests
 *
 * Comprehensive test coverage for execution filtering and processing
 */

import { describe, it, expect } from 'vitest';
import {
  generatePreview,
  filterExecutionData,
  processExecution,
} from '../../../src/services/execution-processor';
import {
  Execution,
  ExecutionStatus,
  ExecutionFilterOptions,
} from '../../../src/types/n8n-api';

/**
 * Test data factories
 */

function createMockExecution(options: {
  id?: string;
  status?: ExecutionStatus;
  nodeData?: Record<string, any>;
  hasError?: boolean;
}): Execution {
  const { id = 'test-exec-1', status = ExecutionStatus.SUCCESS, nodeData = {}, hasError = false } = options;

  return {
    id,
    workflowId: 'workflow-1',
    status,
    mode: 'manual',
    finished: true,
    startedAt: '2024-01-01T10:00:00.000Z',
    stoppedAt: '2024-01-01T10:00:05.000Z',
    data: {
      resultData: {
        runData: nodeData,
        error: hasError ? { message: 'Test error' } : undefined,
      },
    },
  };
}

function createNodeData(itemCount: number, includeError = false) {
  const items = Array.from({ length: itemCount }, (_, i) => ({
    json: {
      id: i + 1,
      name: `Item ${i + 1}`,
      value: Math.random() * 100,
      nested: {
        field1: `value${i}`,
        field2: true,
      },
    },
  }));

  return [
    {
      startTime: Date.now(),
      executionTime: 123,
      data: {
        main: [items],
      },
      error: includeError ? { message: 'Node error' } : undefined,
    },
  ];
}

/**
 * Preview Mode Tests
 */
describe('ExecutionProcessor - Preview Mode', () => {
  it('should generate preview for empty execution', () => {
    const execution = createMockExecution({ nodeData: {} });
    const { preview, recommendation } = generatePreview(execution);

    expect(preview.totalNodes).toBe(0);
    expect(preview.executedNodes).toBe(0);
    expect(preview.estimatedSizeKB).toBe(0);
    expect(recommendation.canFetchFull).toBe(true);
    expect(recommendation.suggestedMode).toBe('full'); // Empty execution is safe to fetch in full
  });

  it('should generate preview with accurate item counts', () => {
    const execution = createMockExecution({
      nodeData: {
        'HTTP Request': createNodeData(50),
        'Filter': createNodeData(12),
      },
    });

    const { preview } = generatePreview(execution);

    expect(preview.totalNodes).toBe(2);
    expect(preview.executedNodes).toBe(2);
    expect(preview.nodes['HTTP Request'].itemCounts.output).toBe(50);
    expect(preview.nodes['Filter'].itemCounts.output).toBe(12);
  });

  it('should extract data structure from nodes', () => {
    const execution = createMockExecution({
      nodeData: {
        'HTTP Request': createNodeData(5),
      },
    });

    const { preview } = generatePreview(execution);
    const structure = preview.nodes['HTTP Request'].dataStructure;

    expect(structure).toHaveProperty('json');
    expect(structure.json).toHaveProperty('id');
    expect(structure.json).toHaveProperty('name');
    expect(structure.json).toHaveProperty('nested');
    expect(structure.json.id).toBe('number');
    expect(structure.json.name).toBe('string');
    expect(typeof structure.json.nested).toBe('object');
  });

  it('should estimate data size', () => {
    const execution = createMockExecution({
      nodeData: {
        'HTTP Request': createNodeData(50),
      },
    });

    const { preview } = generatePreview(execution);

    expect(preview.estimatedSizeKB).toBeGreaterThan(0);
    expect(preview.nodes['HTTP Request'].estimatedSizeKB).toBeGreaterThan(0);
  });

  it('should detect error status in nodes', () => {
    const execution = createMockExecution({
      nodeData: {
        'HTTP Request': createNodeData(5, true),
      },
    });

    const { preview } = generatePreview(execution);

    expect(preview.nodes['HTTP Request'].status).toBe('error');
    expect(preview.nodes['HTTP Request'].error).toBeDefined();
  });

  it('should recommend full mode for small datasets', () => {
    const execution = createMockExecution({
      nodeData: {
        'HTTP Request': createNodeData(5),
      },
    });

    const { recommendation } = generatePreview(execution);

    expect(recommendation.canFetchFull).toBe(true);
    expect(recommendation.suggestedMode).toBe('full');
  });

  it('should recommend filtered mode for large datasets', () => {
    const execution = createMockExecution({
      nodeData: {
        'HTTP Request': createNodeData(100),
      },
    });

    const { recommendation } = generatePreview(execution);

    expect(recommendation.canFetchFull).toBe(false);
    expect(recommendation.suggestedMode).toBe('filtered');
    expect(recommendation.suggestedItemsLimit).toBeGreaterThan(0);
  });

  it('should recommend summary mode for moderate datasets', () => {
    const execution = createMockExecution({
      nodeData: {
        'HTTP Request': createNodeData(30),
      },
    });

    const { recommendation } = generatePreview(execution);

    expect(recommendation.canFetchFull).toBe(false);
    expect(recommendation.suggestedMode).toBe('summary');
  });
});

/**
 * Filtering Mode Tests
 */
describe('ExecutionProcessor - Filtering', () => {
  it('should filter by node names', () => {
    const execution = createMockExecution({
      nodeData: {
        'HTTP Request': createNodeData(10),
        'Filter': createNodeData(5),
        'Set': createNodeData(3),
      },
    });

    const options: ExecutionFilterOptions = {
      mode: 'filtered',
      nodeNames: ['HTTP Request', 'Filter'],
    };

    const result = filterExecutionData(execution, options);

    expect(result.nodes).toHaveProperty('HTTP Request');
    expect(result.nodes).toHaveProperty('Filter');
    expect(result.nodes).not.toHaveProperty('Set');
    expect(result.summary?.executedNodes).toBe(2);
  });

  it('should handle non-existent node names gracefully', () => {
    const execution = createMockExecution({
      nodeData: {
        'HTTP Request': createNodeData(10),
      },
    });

    const options: ExecutionFilterOptions = {
      mode: 'filtered',
      nodeNames: ['NonExistent'],
    };

    const result = filterExecutionData(execution, options);

    expect(Object.keys(result.nodes || {})).toHaveLength(0);
    expect(result.summary?.executedNodes).toBe(0);
  });

  it('should limit items to 0 (structure only)', () => {
    const execution = createMockExecution({
      nodeData: {
        'HTTP Request': createNodeData(50),
      },
    });

    const options: ExecutionFilterOptions = {
      mode: 'filtered',
      itemsLimit: 0,
    };

    const result = filterExecutionData(execution, options);
    const nodeData = result.nodes?.['HTTP Request'];

    expect(nodeData?.data?.metadata.itemsShown).toBe(0);
    expect(nodeData?.data?.metadata.truncated).toBe(true);
    expect(nodeData?.data?.metadata.totalItems).toBe(50);

    // Check that we have structure but no actual values
    const output = nodeData?.data?.output?.[0]?.[0];
    expect(output).toBeDefined();
    expect(typeof output).toBe('object');
  });

  it('should limit items to 2 (default)', () => {
    const execution = createMockExecution({
      nodeData: {
        'HTTP Request': createNodeData(50),
      },
    });

    const options: ExecutionFilterOptions = {
      mode: 'summary',
    };

    const result = filterExecutionData(execution, options);
    const nodeData = result.nodes?.['HTTP Request'];

    expect(nodeData?.data?.metadata.itemsShown).toBe(2);
    expect(nodeData?.data?.metadata.totalItems).toBe(50);
    expect(nodeData?.data?.metadata.truncated).toBe(true);
    expect(nodeData?.data?.output?.[0]).toHaveLength(2);
  });

  it('should limit items to custom value', () => {
    const execution = createMockExecution({
      nodeData: {
        'HTTP Request': createNodeData(50),
      },
    });

    const options: ExecutionFilterOptions = {
      mode: 'filtered',
      itemsLimit: 5,
    };

    const result = filterExecutionData(execution, options);
    const nodeData = result.nodes?.['HTTP Request'];

    expect(nodeData?.data?.metadata.itemsShown).toBe(5);
    expect(nodeData?.data?.metadata.truncated).toBe(true);
    expect(nodeData?.data?.output?.[0]).toHaveLength(5);
  });

  it('should not truncate when itemsLimit is -1 (unlimited)', () => {
    const execution = createMockExecution({
      nodeData: {
        'HTTP Request': createNodeData(50),
      },
    });

    const options: ExecutionFilterOptions = {
      mode: 'filtered',
      itemsLimit: -1,
    };

    const result = filterExecutionData(execution, options);
    const nodeData = result.nodes?.['HTTP Request'];

    expect(nodeData?.data?.metadata.itemsShown).toBe(50);
    expect(nodeData?.data?.metadata.totalItems).toBe(50);
    expect(nodeData?.data?.metadata.truncated).toBe(false);
  });

  it('should not truncate when items are less than limit', () => {
    const execution = createMockExecution({
      nodeData: {
        'HTTP Request': createNodeData(3),
      },
    });

    const options: ExecutionFilterOptions = {
      mode: 'filtered',
      itemsLimit: 5,
    };

    const result = filterExecutionData(execution, options);
    const nodeData = result.nodes?.['HTTP Request'];

    expect(nodeData?.data?.metadata.itemsShown).toBe(3);
    expect(nodeData?.data?.metadata.truncated).toBe(false);
  });

  it('should include input data when requested', () => {
    const execution = createMockExecution({
      nodeData: {
        'HTTP Request': [
          {
            startTime: Date.now(),
            executionTime: 100,
            inputData: [[{ json: { input: 'test' } }]],
            data: {
              main: [[{ json: { output: 'result' } }]],
            },
          },
        ],
      },
    });

    const options: ExecutionFilterOptions = {
      mode: 'filtered',
      includeInputData: true,
    };

    const result = filterExecutionData(execution, options);
    const nodeData = result.nodes?.['HTTP Request'];

    expect(nodeData?.data?.input).toBeDefined();
    expect(nodeData?.data?.input?.[0]?.[0]?.json?.input).toBe('test');
  });

  it('should not include input data by default', () => {
    const execution = createMockExecution({
      nodeData: {
        'HTTP Request': [
          {
            startTime: Date.now(),
            executionTime: 100,
            inputData: [[{ json: { input: 'test' } }]],
            data: {
              main: [[{ json: { output: 'result' } }]],
            },
          },
        ],
      },
    });

    const options: ExecutionFilterOptions = {
      mode: 'filtered',
    };

    const result = filterExecutionData(execution, options);
    const nodeData = result.nodes?.['HTTP Request'];

    expect(nodeData?.data?.input).toBeUndefined();
  });
});

/**
 * Mode Tests
 */
describe('ExecutionProcessor - Modes', () => {
  it('should handle preview mode', () => {
    const execution = createMockExecution({
      nodeData: {
        'HTTP Request': createNodeData(50),
      },
    });

    const result = filterExecutionData(execution, { mode: 'preview' });

    expect(result.mode).toBe('preview');
    expect(result.preview).toBeDefined();
    expect(result.recommendation).toBeDefined();
    expect(result.nodes).toBeUndefined();
  });

  it('should handle summary mode', () => {
    const execution = createMockExecution({
      nodeData: {
        'HTTP Request': createNodeData(50),
      },
    });

    const result = filterExecutionData(execution, { mode: 'summary' });

    expect(result.mode).toBe('summary');
    expect(result.summary).toBeDefined();
    expect(result.nodes).toBeDefined();
    expect(result.nodes?.['HTTP Request']?.data?.metadata.itemsShown).toBe(2);
  });

  it('should handle filtered mode', () => {
    const execution = createMockExecution({
      nodeData: {
        'HTTP Request': createNodeData(50),
      },
    });

    const result = filterExecutionData(execution, {
      mode: 'filtered',
      itemsLimit: 5,
    });

    expect(result.mode).toBe('filtered');
    expect(result.summary).toBeDefined();
    expect(result.nodes?.['HTTP Request']?.data?.metadata.itemsShown).toBe(5);
  });

  it('should handle full mode', () => {
    const execution = createMockExecution({
      nodeData: {
        'HTTP Request': createNodeData(50),
      },
    });

    const result = filterExecutionData(execution, { mode: 'full' });

    expect(result.mode).toBe('full');
    expect(result.nodes?.['HTTP Request']?.data?.metadata.itemsShown).toBe(50);
    expect(result.nodes?.['HTTP Request']?.data?.metadata.truncated).toBe(false);
  });
});

/**
 * Edge Cases
 */
describe('ExecutionProcessor - Edge Cases', () => {
  it('should handle execution with no data', () => {
    const execution: Execution = {
      id: 'test-1',
      workflowId: 'workflow-1',
      status: ExecutionStatus.SUCCESS,
      mode: 'manual',
      finished: true,
      startedAt: '2024-01-01T10:00:00.000Z',
      stoppedAt: '2024-01-01T10:00:05.000Z',
    };

    const result = filterExecutionData(execution, { mode: 'summary' });

    expect(result.summary?.totalNodes).toBe(0);
    expect(result.summary?.executedNodes).toBe(0);
  });

  it('should handle execution with error', () => {
    const execution = createMockExecution({
      nodeData: {
        'HTTP Request': createNodeData(5),
      },
      hasError: true,
    });

    const result = filterExecutionData(execution, { mode: 'summary' });

    expect(result.error).toBeDefined();
  });

  it('should handle empty node data arrays', () => {
    const execution = createMockExecution({
      nodeData: {
        'HTTP Request': [],
      },
    });

    const result = filterExecutionData(execution, { mode: 'summary' });

    expect(result.nodes?.['HTTP Request']).toBeDefined();
    expect(result.nodes?.['HTTP Request'].itemsOutput).toBe(0);
  });

  it('should handle nested data structures', () => {
    const execution = createMockExecution({
      nodeData: {
        'HTTP Request': [
          {
            startTime: Date.now(),
            executionTime: 100,
            data: {
              main: [[{
                json: {
                  deeply: {
                    nested: {
                      structure: {
                        value: 'test',
                        array: [1, 2, 3],
                      },
                    },
                  },
                },
              }]],
            },
          },
        ],
      },
    });

    const { preview } = generatePreview(execution);
    const structure = preview.nodes['HTTP Request'].dataStructure;

    expect(structure.json.deeply).toBeDefined();
    expect(typeof structure.json.deeply).toBe('object');
  });

  it('should calculate duration correctly', () => {
    const execution = createMockExecution({
      nodeData: {
        'HTTP Request': createNodeData(5),
      },
    });

    const result = filterExecutionData(execution, { mode: 'summary' });

    expect(result.duration).toBe(5000); // 5 seconds
  });

  it('should handle execution without stop time', () => {
    const execution: Execution = {
      id: 'test-1',
      workflowId: 'workflow-1',
      status: ExecutionStatus.WAITING,
      mode: 'manual',
      finished: false,
      startedAt: '2024-01-01T10:00:00.000Z',
      data: {
        resultData: {
          runData: {},
        },
      },
    };

    const result = filterExecutionData(execution, { mode: 'summary' });

    expect(result.duration).toBeUndefined();
    expect(result.finished).toBe(false);
  });
});

/**
 * processExecution Tests
 */
describe('ExecutionProcessor - processExecution', () => {
  it('should return original execution when no options provided', () => {
    const execution = createMockExecution({
      nodeData: {
        'HTTP Request': createNodeData(5),
      },
    });

    const result = processExecution(execution, {});

    expect(result).toBe(execution);
  });

  it('should process when mode is specified', () => {
    const execution = createMockExecution({
      nodeData: {
        'HTTP Request': createNodeData(5),
      },
    });

    const result = processExecution(execution, { mode: 'preview' });

    expect(result).not.toBe(execution);
    expect((result as any).mode).toBe('preview');
  });

  it('should process when filtering options are provided', () => {
    const execution = createMockExecution({
      nodeData: {
        'HTTP Request': createNodeData(5),
        'Filter': createNodeData(3),
      },
    });

    const result = processExecution(execution, { nodeNames: ['HTTP Request'] });

    expect(result).not.toBe(execution);
    expect((result as any).nodes).toHaveProperty('HTTP Request');
    expect((result as any).nodes).not.toHaveProperty('Filter');
  });
});

/**
 * Summary Statistics Tests
 */
describe('ExecutionProcessor - Summary Statistics', () => {
  it('should calculate hasMoreData correctly', () => {
    const execution = createMockExecution({
      nodeData: {
        'HTTP Request': createNodeData(50),
      },
    });

    const result = filterExecutionData(execution, {
      mode: 'summary',
      itemsLimit: 2,
    });

    expect(result.summary?.hasMoreData).toBe(true);
  });

  it('should set hasMoreData to false when all data is included', () => {
    const execution = createMockExecution({
      nodeData: {
        'HTTP Request': createNodeData(2),
      },
    });

    const result = filterExecutionData(execution, {
      mode: 'summary',
      itemsLimit: 5,
    });

    expect(result.summary?.hasMoreData).toBe(false);
  });

  it('should count total items correctly across multiple nodes', () => {
    const execution = createMockExecution({
      nodeData: {
        'HTTP Request': createNodeData(10),
        'Filter': createNodeData(5),
        'Set': createNodeData(3),
      },
    });

    const result = filterExecutionData(execution, { mode: 'summary' });

    expect(result.summary?.totalItems).toBe(18);
  });
});
