/**
 * Integration Tests for CodexLens with actual file operations
 *
 * These tests create temporary files and directories to test
 * the full indexing and search workflow.
 */

import { describe, it, before, after } from 'node:test';
import assert from 'node:assert';
import { dirname, join } from 'path';
import { fileURLToPath } from 'url';
import { existsSync, mkdirSync, rmSync, writeFileSync, readdirSync } from 'fs';
import { tmpdir } from 'os';

const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);

// Import the codex-lens module
const codexLensPath = new URL('../dist/tools/codex-lens.js', import.meta.url).href;

describe('CodexLens Full Integration Tests', async () => {
  let codexLensModule;
  let testDir;
  let isReady = false;

  before(async () => {
    try {
      codexLensModule = await import(codexLensPath);

      // Check if CodexLens is installed
      const status = await codexLensModule.checkVenvStatus();
      isReady = status.ready;

      if (!isReady) {
        console.log('CodexLens not installed - some integration tests will be skipped');
        return;
      }

      // Create temporary test directory
      testDir = join(tmpdir(), `codexlens-test-${Date.now()}`);
      mkdirSync(testDir, { recursive: true });

      // Create test Python files
      writeFileSync(join(testDir, 'main.py'), `
"""Main module for testing."""

def hello_world():
    """Say hello to the world."""
    print("Hello, World!")
    return "hello"

def calculate_sum(a, b):
    """Calculate sum of two numbers."""
    return a + b

class Calculator:
    """A simple calculator class."""

    def __init__(self):
        self.result = 0

    def add(self, value):
        """Add value to result."""
        self.result += value
        return self.result

    def subtract(self, value):
        """Subtract value from result."""
        self.result -= value
        return self.result
`);

      writeFileSync(join(testDir, 'utils.py'), `
"""Utility functions."""

def format_string(text):
    """Format a string."""
    return text.strip().lower()

def validate_email(email):
    """Validate email format."""
    return "@" in email and "." in email

async def fetch_data(url):
    """Fetch data from URL (async)."""
    pass
`);

      // Create test JavaScript file
      writeFileSync(join(testDir, 'app.js'), `
/**
 * Main application module
 */

function initApp() {
  console.log('App initialized');
}

const processData = async (data) => {
  return data.map(item => item.value);
};

class Application {
  constructor(name) {
    this.name = name;
  }

  start() {
    console.log(\`Starting \${this.name}\`);
  }
}

export { initApp, processData, Application };
`);

      console.log(`Test directory created at: ${testDir}`);
    } catch (err) {
      console.log('Setup failed:', err.message);
    }
  });

  after(async () => {
    // Cleanup test directory
    if (testDir && existsSync(testDir)) {
      try {
        rmSync(testDir, { recursive: true, force: true });
        console.log('Test directory cleaned up');
      } catch (err) {
        console.log('Cleanup failed:', err.message);
      }
    }
  });

  describe('Index Initialization', () => {
    it('should initialize index for test directory', async () => {
      if (!isReady || !testDir) {
        console.log('Skipping: CodexLens not ready or test dir not created');
        return;
      }

      const result = await codexLensModule.codexLensTool.execute({
        action: 'init',
        path: testDir
      });

      assert.ok(typeof result === 'object', 'Result should be an object');
      assert.ok('success' in result, 'Result should have success property');

      if (result.success) {
        // CodexLens stores indexes in the global data directory (e.g. ~/.codexlens/indexes)
        // rather than creating a per-project ".codexlens" folder.
        assert.ok(true);
      }
    });

    it('should create index.db file', async () => {
      if (!isReady || !testDir) {
        console.log('Skipping: CodexLens not ready or test dir not created');
        return;
      }

      const indexDb = join(testDir, '.codexlens', 'index.db');

      // May need to wait for previous init to complete
      // Index.db should exist after successful init
      if (existsSync(join(testDir, '.codexlens'))) {
        // Check files in .codexlens directory
        const files = readdirSync(join(testDir, '.codexlens'));
        console.log('.codexlens contents:', files);
      }
    });
  });

  describe('Status Query', () => {
    it('should return index status for test directory', async () => {
      if (!isReady || !testDir) {
        console.log('Skipping: CodexLens not ready or test dir not created');
        return;
      }

      const result = await codexLensModule.codexLensTool.execute({
        action: 'status',
        path: testDir
      });

      assert.ok(typeof result === 'object', 'Result should be an object');
      console.log('Index status:', JSON.stringify(result, null, 2));

      if (result.success) {
        // Navigate nested structure: result.status.result or result.result
        const statusData = result.status?.result || result.result || result.status || result;
        const hasIndexInfo = (
          'files' in statusData ||
          'db_path' in statusData ||
          result.output ||
          (result.status && 'success' in result.status)
        );
        assert.ok(hasIndexInfo, 'Status should contain index information or raw output');
      }
    });
  });

  describe('Symbol Extraction', () => {
    it('should extract symbols from Python file', async () => {
      if (!isReady || !testDir) {
        console.log('Skipping: CodexLens not ready or test dir not created');
        return;
      }

      const result = await codexLensModule.codexLensTool.execute({
        action: 'symbol',
        file: join(testDir, 'main.py')
      });

      assert.ok(typeof result === 'object', 'Result should be an object');

      if (result.success) {
        console.log('Symbols found:', result.symbols || result.output);

        // Parse output if needed
        let symbols = result.symbols;
        if (!symbols && result.output) {
          try {
            const parsed = JSON.parse(result.output);
            symbols = parsed.result?.file?.symbols || parsed.symbols;
          } catch {
            // Keep raw output
          }
        }

        if (symbols && Array.isArray(symbols)) {
          // Check for expected symbols
          const symbolNames = symbols.map(s => s.name);
          assert.ok(symbolNames.includes('hello_world') || symbolNames.some(n => n.includes('hello')),
            'Should find hello_world function');
          assert.ok(symbolNames.includes('Calculator') || symbolNames.some(n => n.includes('Calc')),
            'Should find Calculator class');
        }
      }
    });

    it('should extract symbols from JavaScript file', async () => {
      if (!isReady || !testDir) {
        console.log('Skipping: CodexLens not ready or test dir not created');
        return;
      }

      const result = await codexLensModule.codexLensTool.execute({
        action: 'symbol',
        file: join(testDir, 'app.js')
      });

      assert.ok(typeof result === 'object', 'Result should be an object');

      if (result.success) {
        console.log('JS Symbols found:', result.symbols || result.output);
      }
    });
  });

  describe('Full-Text Search', () => {
    it('should search for text in indexed files', async () => {
      if (!isReady || !testDir) {
        console.log('Skipping: CodexLens not ready or test dir not created');
        return;
      }

      // First ensure index is initialized
      await codexLensModule.codexLensTool.execute({
        action: 'init',
        path: testDir
      });

      const result = await codexLensModule.codexLensTool.execute({
        action: 'search',
        query: 'hello',
        path: testDir,
        limit: 10
      });

      assert.ok(typeof result === 'object', 'Result should be an object');

      if (result.success) {
        console.log('Search results:', result.results || result.output);
      }
    });

    it('should search for class names', async () => {
      if (!isReady || !testDir) {
        console.log('Skipping: CodexLens not ready or test dir not created');
        return;
      }

      const result = await codexLensModule.codexLensTool.execute({
        action: 'search',
        query: 'Calculator',
        path: testDir,
        limit: 10
      });

      assert.ok(typeof result === 'object', 'Result should be an object');

      if (result.success) {
        console.log('Class search results:', result.results || result.output);
      }
    });
  });

  describe('Incremental Update', () => {
    it('should update index when file changes', async () => {
      if (!isReady || !testDir) {
        console.log('Skipping: CodexLens not ready or test dir not created');
        return;
      }

      // Create a new file
      const newFile = join(testDir, 'new_module.py');
      writeFileSync(newFile, `
def new_function():
    """A newly added function."""
    return "new"
`);

      const result = await codexLensModule.codexLensTool.execute({
        action: 'update',
        files: [newFile],
        path: testDir
      });

      assert.ok(typeof result === 'object', 'Result should be an object');

      if (result.success) {
        console.log('Update result:', result.updateResult || result.output);
      }
    });

    it('should handle deleted files in update', async () => {
      if (!isReady || !testDir) {
        console.log('Skipping: CodexLens not ready or test dir not created');
        return;
      }

      // Reference a non-existent file
      const deletedFile = join(testDir, 'deleted_file.py');

      const result = await codexLensModule.codexLensTool.execute({
        action: 'update',
        files: [deletedFile],
        path: testDir
      });

      assert.ok(typeof result === 'object', 'Result should be an object');
      // Should handle gracefully without crashing
    });
  });
});

describe('CodexLens CLI Commands via executeCodexLens', async () => {
  let codexLensModule;
  let isReady = false;

  before(async () => {
    try {
      codexLensModule = await import(codexLensPath);
      const status = await codexLensModule.checkVenvStatus();
      isReady = status.ready;
    } catch (err) {
      console.log('Setup failed:', err.message);
    }
  });

  it('should execute --version command', async () => {
    if (!isReady) {
      console.log('Skipping: CodexLens not ready');
      return;
    }

    // Note: codexlens may not have --version, use --help instead
    const result = await codexLensModule.executeCodexLens(['--help']);
    assert.ok(typeof result === 'object');

    if (result.success) {
      assert.ok(result.output, 'Should have output');
    }
  });

  it('should execute status --json command', async () => {
    if (!isReady) {
      console.log('Skipping: CodexLens not ready');
      return;
    }

    const result = await codexLensModule.executeCodexLens(['status', '--json'], {
      cwd: __dirname
    });

    assert.ok(typeof result === 'object');

    if (result.success && result.output) {
      // Try to parse JSON output
      try {
        const parsed = JSON.parse(result.output);
        assert.ok(typeof parsed === 'object', 'Output should be valid JSON');
      } catch {
        // Output might not be JSON if index doesn't exist
        console.log('Status output (non-JSON):', result.output);
      }
    }
  });

  it('should handle inspect command', async () => {
    if (!isReady) {
      console.log('Skipping: CodexLens not ready');
      return;
    }

    // Use this test file as input
    const testFile = join(__dirname, 'codex-lens.test.js');
    if (!existsSync(testFile)) {
      console.log('Skipping: Test file not found');
      return;
    }

    const result = await codexLensModule.executeCodexLens([
      'inspect', testFile, '--json'
    ]);

    assert.ok(typeof result === 'object');

    if (result.success) {
      console.log('Inspect result received');
    }
  });
});

describe('CodexLens Workspace Detection', async () => {
  let codexLensModule;
  let isReady = false;

  before(async () => {
    try {
      codexLensModule = await import(codexLensPath);
      const status = await codexLensModule.checkVenvStatus();
      isReady = status.ready;
    } catch (err) {
      console.log('Setup failed:', err.message);
    }
  });

  it('should detect existing workspace', async () => {
    if (!isReady) {
      console.log('Skipping: CodexLens not ready');
      return;
    }

    // Try to get status from project root where .codexlens might exist
    const projectRoot = join(__dirname, '..', '..');

    const result = await codexLensModule.codexLensTool.execute({
      action: 'status',
      path: projectRoot
    });

    assert.ok(typeof result === 'object');
    console.log('Project root status:', result.success ? 'Found' : 'Not found');
  });

  it('should use global database when workspace not found', async () => {
    if (!isReady) {
      console.log('Skipping: CodexLens not ready');
      return;
    }

    // Use a path that definitely won't have .codexlens
    const tempPath = tmpdir();

    const result = await codexLensModule.codexLensTool.execute({
      action: 'status',
      path: tempPath
    });

    assert.ok(typeof result === 'object');
    // Should fall back to global database
  });
});
