import { describe, it, expect } from 'vitest';
import {
  calculateEntryPointScore,
  isTestFile,
  isUtilityFile,
} from '../../src/core/ingestion/entry-point-scoring.js';

describe('calculateEntryPointScore', () => {
  describe('base scoring', () => {
    it('returns 0 for functions with no outgoing calls', () => {
      const result = calculateEntryPointScore('handler', 'typescript', true, 0, 0);
      expect(result.score).toBe(0);
      expect(result.reasons).toContain('no-outgoing-calls');
    });

    it('calculates base score as calleeCount / (callerCount + 1)', () => {
      const result = calculateEntryPointScore('doStuff', 'typescript', false, 0, 5);
      // base = 5 / (0 + 1) = 5, no export bonus, no name bonus
      expect(result.score).toBe(5);
    });

    it('reduces score for functions with many callers', () => {
      const few = calculateEntryPointScore('doStuff', 'typescript', false, 1, 5);
      const many = calculateEntryPointScore('doStuff', 'typescript', false, 10, 5);
      expect(few.score).toBeGreaterThan(many.score);
    });
  });

  describe('export multiplier', () => {
    it('applies 2.0 multiplier for exported functions', () => {
      const exported = calculateEntryPointScore('doStuff', 'typescript', true, 0, 4);
      const notExported = calculateEntryPointScore('doStuff', 'typescript', false, 0, 4);
      expect(exported.score).toBe(notExported.score * 2);
      expect(exported.reasons).toContain('exported');
    });

    it('does not add exported reason when not exported', () => {
      const result = calculateEntryPointScore('doStuff', 'typescript', false, 0, 4);
      expect(result.reasons).not.toContain('exported');
    });
  });

  describe('universal name patterns', () => {
    it.each(['main', 'init', 'bootstrap', 'start', 'run', 'setup', 'configure'])(
      'recognizes "%s" as entry point pattern',
      (name) => {
        const result = calculateEntryPointScore(name, 'typescript', false, 0, 3);
        expect(result.reasons).toContain('entry-pattern');
      },
    );

    it.each([
      'handleLogin',
      'handleSubmit',
      'onClick',
      'onSubmit',
      'RequestHandler',
      'UserController',
      'processPayment',
      'executeQuery',
      'performAction',
      'dispatchEvent',
      'triggerAction',
      'fireEvent',
      'emitEvent',
    ])('recognizes "%s" as entry point pattern', (name) => {
      const result = calculateEntryPointScore(name, 'typescript', false, 0, 3);
      expect(result.reasons).toContain('entry-pattern');
    });

    it('applies 1.5x name multiplier for entry patterns', () => {
      const matching = calculateEntryPointScore('handleLogin', 'typescript', false, 0, 4);
      const plain = calculateEntryPointScore('doStuff', 'typescript', false, 0, 4);
      // matching gets 1.5x, plain gets 1.0x
      expect(matching.score).toBe(plain.score * 1.5);
    });
  });

  describe('language-specific patterns', () => {
    it('recognizes React hooks for TypeScript', () => {
      const result = calculateEntryPointScore('useEffect', 'typescript', false, 0, 2);
      expect(result.reasons).toContain('entry-pattern');
    });

    it('recognizes React hooks for JavaScript', () => {
      const result = calculateEntryPointScore('useState', 'javascript', false, 0, 2);
      expect(result.reasons).toContain('entry-pattern');
    });

    it('recognizes Python REST patterns', () => {
      const result = calculateEntryPointScore('get_users', 'python', false, 0, 2);
      expect(result.reasons).toContain('entry-pattern');
    });

    it('recognizes Java servlet patterns', () => {
      const result = calculateEntryPointScore('doGet', 'java', false, 0, 2);
      expect(result.reasons).toContain('entry-pattern');
    });

    it('recognizes Go handler patterns', () => {
      const result = calculateEntryPointScore('NewServer', 'go', false, 0, 2);
      expect(result.reasons).toContain('entry-pattern');
    });

    it('recognizes Rust entry patterns', () => {
      const result = calculateEntryPointScore('handle_request', 'rust', false, 0, 2);
      expect(result.reasons).toContain('entry-pattern');
    });

    it('recognizes Swift UIKit lifecycle', () => {
      const result = calculateEntryPointScore('viewDidLoad', 'swift', false, 0, 2);
      expect(result.reasons).toContain('entry-pattern');
    });

    it('recognizes Swift SwiftUI body', () => {
      const result = calculateEntryPointScore('body', 'swift', false, 0, 2);
      expect(result.reasons).toContain('entry-pattern');
    });

    it('recognizes PHP Laravel patterns', () => {
      // __invoke starts with '_' which matches utility pattern first
      const result = calculateEntryPointScore('handle', 'php', false, 0, 2);
      expect(result.reasons).toContain('entry-pattern');
    });

    it('recognizes PHP RESTful resource methods', () => {
      const result = calculateEntryPointScore('index', 'php', false, 0, 2);
      expect(result.reasons).toContain('entry-pattern');
    });

    it('recognizes C# ASP.NET patterns', () => {
      const result = calculateEntryPointScore('GetUsers', 'csharp', false, 0, 2);
      expect(result.reasons).toContain('entry-pattern');
    });

    it('recognizes C main entry point', () => {
      const result = calculateEntryPointScore('main', 'c', false, 0, 2);
      expect(result.reasons).toContain('entry-pattern');
    });

    // C-specific patterns
    it.each([
      'init_server',
      'server_init',
      'start_server',
      'handle_request',
      'signal_handler',
      'event_callback',
      'cmd_new_window',
      'server_start',
      'client_connect',
      'session_create',
      'window_resize',
    ])('recognizes C pattern "%s"', (name) => {
      const result = calculateEntryPointScore(name, 'c', false, 0, 2);
      expect(result.reasons).toContain('entry-pattern');
    });

    // C++-specific patterns
    it.each([
      'CreateInstance',
      'create_session',
      'Run',
      'run',
      'Start',
      'start',
      'OnEventReceived',
      'on_click',
    ])('recognizes C++ pattern "%s"', (name) => {
      const result = calculateEntryPointScore(name, 'cpp', false, 0, 2);
      expect(result.reasons).toContain('entry-pattern');
    });

    // Kotlin-specific patterns (Android lifecycle, ViewModel, Service)
    it.each([
      'onCreate',
      'onStart',
      'onResume',
      'onPause',
      'onStop',
      'onDestroy',
      'doWork',
      'createComponent',
      'buildGraph',
      'UserViewModel',
      'module',
      'AuthService',
    ])('recognizes Kotlin pattern "%s"', (name) => {
      const result = calculateEntryPointScore(name, 'kotlin', false, 0, 2);
      expect(result.reasons).toContain('entry-pattern');
    });

    // Dart-specific patterns (Flutter widget lifecycle, BLoC)
    // Note: didChangeDependencies/didUpdateWidget/mapEventToState are listed in
    // the Dart provider but pre-empted by UTILITY_PATTERNS (`did[A-Z]`, `^map`)
    // before the entry-pattern check, so they are not asserted here.
    it.each(['main', 'build', 'createState', 'initState', 'dispose', 'runApp', 'onEvent'])(
      'recognizes Dart pattern "%s"',
      (name) => {
        const result = calculateEntryPointScore(name, 'dart', false, 0, 2);
        expect(result.reasons).toContain('entry-pattern');
      },
    );

    // Ruby-specific patterns (Rails callable/job/service objects)
    it.each(['call', 'perform', 'execute'])('recognizes Ruby pattern "%s"', (name) => {
      const result = calculateEntryPointScore(name, 'ruby', false, 0, 2);
      expect(result.reasons).toContain('entry-pattern');
    });
  });

  describe('utility pattern penalty', () => {
    it.each([
      'getUser',
      'setName',
      'isValid',
      'hasPermission',
      'canEdit',
      'formatDate',
      'parseJSON',
      'validateInput',
      'toString',
      'fromJSON',
      'encodeBase64',
      'serializeData',
      'cloneDeep',
      'mergeObjects',
    ])('penalizes utility function "%s"', (name) => {
      const result = calculateEntryPointScore(name, 'typescript', false, 0, 3);
      expect(result.reasons).toContain('utility-pattern');
      // 0.3 multiplier
      const plain = calculateEntryPointScore('doStuff', 'typescript', false, 0, 3);
      expect(result.score).toBeLessThan(plain.score);
    });

    it('penalizes private-by-convention functions', () => {
      const result = calculateEntryPointScore('_internal', 'typescript', false, 0, 3);
      expect(result.reasons).toContain('utility-pattern');
    });
  });

  describe('framework detection from path', () => {
    it('boosts Next.js page entry points', () => {
      const result = calculateEntryPointScore(
        'render',
        'typescript',
        true,
        0,
        3,
        'pages/users.tsx',
      );
      expect(result.reasons.some((r) => r.includes('framework:'))).toBe(true);
      expect(result.score).toBeGreaterThan(0);
    });

    it('does not apply framework bonus for non-framework paths', () => {
      const result = calculateEntryPointScore(
        'render',
        'typescript',
        true,
        0,
        3,
        'src/lib/utils.ts',
      );
      expect(result.reasons.every((r) => !r.includes('framework:'))).toBe(true);
    });
  });

  describe('combined scoring', () => {
    it('multiplies all factors together', () => {
      // handleLogin: entry pattern (1.5x) + exported (2.0x) + base
      const result = calculateEntryPointScore(
        'handleLogin',
        'typescript',
        true,
        0,
        4,
        'routes/auth.ts',
      );
      expect(result.score).toBeGreaterThan(0);
      expect(result.reasons).toContain('exported');
      expect(result.reasons).toContain('entry-pattern');
    });
  });
});

describe('isTestFile', () => {
  it.each([
    'src/utils.test.ts',
    'src/utils.spec.ts',
    '__tests__/utils.ts',
    '__mocks__/api.ts',
    'src/test/integration/db.ts',
    'src/tests/unit/helper.ts',
    'src/testing/setup.ts',
    'lib/test_utils.py',
    'pkg/handler_test.go',
    'src/test/java/com/example/Test.java',
    'MyViewTests.swift',
    'MyViewTest.swift',
    'UITests/LoginTest.swift',
    'App.Tests/MyTest.cs',
    'tests/Feature/UserTest.php',
    'tests/Unit/AuthSpec.php',
  ])('returns true for test file "%s"', (filePath) => {
    expect(isTestFile(filePath)).toBe(true);
  });

  it.each([
    'src/utils.ts',
    'src/controllers/auth.ts',
    'src/main.py',
    'cmd/server.go',
    'src/main/java/App.java',
  ])('returns false for non-test file "%s"', (filePath) => {
    expect(isTestFile(filePath)).toBe(false);
  });

  it('normalizes Windows backslashes', () => {
    expect(isTestFile('src\\__tests__\\utils.ts')).toBe(true);
  });
});

describe('isUtilityFile', () => {
  it.each([
    'src/utils/format.ts',
    'src/util/helpers.ts',
    'src/helpers/date.ts',
    'src/helper/string.ts',
    'src/common/types.ts',
    'src/shared/constants.ts',
    'src/lib/crypto.ts',
    'src/utils.ts',
    'src/utils.js',
    'src/helpers.ts',
    'lib/date_utils.py',
    'lib/date_helpers.py',
  ])('returns true for utility file "%s"', (filePath) => {
    expect(isUtilityFile(filePath)).toBe(true);
  });

  it.each(['src/controllers/auth.ts', 'src/routes/api.ts', 'src/main.ts', 'src/app.ts'])(
    'returns false for non-utility file "%s"',
    (filePath) => {
      expect(isUtilityFile(filePath)).toBe(false);
    },
  );
});
