import { describe, it, expect } from 'vitest';
import { ALL_NODE_TYPES, ACTIVITY_NODE_TYPES, NodeType } from '../dsl/node-types';
import * as path from 'path';
import * as fs from 'fs';

// ─── Consistency with seed file ──────────────────────────────────────────────

describe('Node Type Consistency', () => {
  // Read the seed file and extract types
  const seedFilePath = path.resolve(__dirname, '../../../../server/seed/workflow-node-types.ts');
  const seedContent = fs.readFileSync(seedFilePath, 'utf-8');

  // Extract only top-level node type identifiers (at 4-space indent, not JSON Schema "type" fields)
  // Pattern: lines like `    type: 'cron_trigger',` (4 spaces indent, contains underscore or known names)
  const typeRegex = /^\s{4}type:\s*'([a-z][a-z_]+)'/gm;
  const seedTypes: string[] = [];
  let match: RegExpExecArray | null;
  while ((match = typeRegex.exec(seedContent)) !== null) {
    // Exclude JSON Schema type values like 'object', 'string', 'number', 'array', 'boolean'
    const jsonSchemaTypes = ['object', 'string', 'number', 'array', 'boolean', 'integer'];
    if (!jsonSchemaTypes.includes(match[1])) {
      seedTypes.push(match[1]);
    }
  }

  it('seed file has node types defined', () => {
    expect(seedTypes.length).toBeGreaterThan(0);
  });

  it('every seed node type exists in ALL_NODE_TYPES', () => {
    const allTypes = ALL_NODE_TYPES as readonly string[];
    const missing = seedTypes.filter((t) => !allTypes.includes(t));
    expect(missing).toEqual([]);
  });

  it('every ALL_NODE_TYPES entry exists in seed file', () => {
    const missing = ALL_NODE_TYPES.filter((t) => !seedTypes.includes(t));
    expect(missing).toEqual([]);
  });

  it('no duplicates in ALL_NODE_TYPES', () => {
    const set = new Set(ALL_NODE_TYPES);
    expect(set.size).toBe(ALL_NODE_TYPES.length);
  });

  it('ACTIVITY_NODE_TYPES is a subset of ALL_NODE_TYPES', () => {
    const allTypes = ALL_NODE_TYPES as readonly string[];
    for (const t of ACTIVITY_NODE_TYPES) {
      expect(allTypes).toContain(t);
    }
  });
});

// ─── Consistency with executeNode switch ─────────────────────────────────────

describe('executeNode switch coverage', () => {
  const workflowFilePath = path.resolve(__dirname, '../workflows/dsl-workflow.ts');
  const workflowContent = fs.readFileSync(workflowFilePath, 'utf-8');

  it('every NodeType has a case in executeNode', () => {
    const missingCases: string[] = [];
    for (const nodeType of ALL_NODE_TYPES) {
      // Check for case 'nodeType': pattern
      if (!workflowContent.includes(`case '${nodeType}'`)) {
        missingCases.push(nodeType);
      }
    }
    expect(missingCases).toEqual([]);
  });
});

// ─── Consistency with activity files ─────────────────────────────────────────

describe('Activity file coverage', () => {
  const activitiesIndexPath = path.resolve(__dirname, '../activities/index.ts');
  const activitiesContent = fs.readFileSync(activitiesIndexPath, 'utf-8');

  // Map node types to expected activity function names
  const nodeTypeToActivity: Record<string, string> = {
    http_request: 'httpRequest',
    db_query: 'dbQuery',
    db_execute: 'dbExecute',
    send_notification: 'sendNotification',
    code: 'codeExecute',
  };

  it('every ACTIVITY_NODE_TYPE has a corresponding exported activity', () => {
    const missing: string[] = [];
    for (const nodeType of ACTIVITY_NODE_TYPES) {
      const expectedFn = nodeTypeToActivity[nodeType];
      if (!expectedFn) {
        missing.push(`${nodeType}: no mapping defined in test`);
        continue;
      }
      if (!activitiesContent.includes(expectedFn)) {
        missing.push(`${nodeType}: activity "${expectedFn}" not found in activities/index.ts`);
      }
    }
    expect(missing).toEqual([]);
  });
});
