import { describe, it, expect, vi, beforeEach, afterEach, type MockInstance, type Mock } from 'vitest';
import { normalizeToolParams, startServer } from '../lib/server'; // Import startServer
import { IndexingStatusReport } from '../lib/repository'; // For mock status
import type * as httpModule from 'http'; // For types
// Import actual modules to be mocked
import http from 'http';
import axios from 'axios'; // Import axios
import * as net from 'net'; // For net.ListenOptions

// Define stable mock for McpServer.connect
const mcpConnectStableMock = vi.fn(); 

// Mock dependencies
vi.mock('@modelcontextprotocol/sdk/server/mcp.js', async (importOriginal) => {
  // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
  const actual = await importOriginal() as typeof import('@modelcontextprotocol/sdk/server/mcp.js');
  return {
    ...actual,
    McpServer: vi.fn().mockImplementation(() => ({
      connect: mcpConnectStableMock, // Use stable mock
    tool: vi.fn(),
    resource: vi.fn(),
    prompt: vi.fn(), // Added prompt mock
    })),
    ResourceTemplate: vi.fn().mockImplementation((uriTemplate: string, _options: unknown): { uriTemplate: string } => {
    // Basic mock for ResourceTemplate constructor
    return { uriTemplate };
    })
  };
});

// Corrected mock path for configService and logger
vi.mock('../lib/config-service', async (importOriginal) => {
  // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
  const actual = await importOriginal() as typeof import('../lib/config-service');
  return {
    ...actual, // Spread actual to keep non-mocked parts if any, or specific exports
    configService: {
      // Provide all properties and methods accessed by server.ts
      // Basic defaults, specific tests can override via vi.spyOn or direct mock value changes
      HTTP_PORT: 3001,
      OLLAMA_HOST: 'http://127.0.0.1:11434',
      QDRANT_HOST: 'http://127.0.0.1:6333',
      COLLECTION_NAME: 'test-collection',
      SUGGESTION_MODEL: 'test-model',
      SUGGESTION_PROVIDER: 'ollama',
      EMBEDDING_MODEL: 'nomic-embed-text:v1.5',
      EMBEDDING_PROVIDER: 'ollama',
      DEEPSEEK_API_KEY: '',
      OPENAI_API_KEY: '',
      GEMINI_API_KEY: '',
      CLAUDE_API_KEY: '',
      VERSION: 'test-version', // Add if server.ts uses configService.VERSION
      reloadConfigsFromFile: vi.fn(),
      // Add any other properties/methods from ConfigService that server.ts uses
      // For example, if it uses specific model names for summarization/refinement:
      SUMMARIZATION_MODEL: 'test-summary-model',
      REFINEMENT_MODEL: 'test-refinement-model',
      // Add any other config values used in startServer
      MAX_SNIPPET_LENGTH: 500,
    },
    logger: {
      info: vi.fn(),
      warn: vi.fn(),
      error: vi.fn(),
      debug: vi.fn(), // Add debug if used
      add: vi.fn(), // If logger.add is called
    }
  };
});

vi.mock('../lib/ollama', () => ({
  checkOllama: vi.fn().mockResolvedValue(true),
  checkOllamaModel: vi.fn().mockResolvedValue(true),
  // generateEmbedding: vi.fn().mockResolvedValue([0.1, 0.2, 0.3]), // Not directly used by startServer
  // generateSuggestion: vi.fn().mockResolvedValue('Test suggestion'), // Not directly used by startServer
  // summarizeSnippet: vi.fn().mockResolvedValue('Test summary') // Not directly used by startServer
}));

import type { QdrantClient } from '@qdrant/js-client-rest';

vi.mock('../lib/qdrant', () => ({
  initializeQdrant: vi.fn<() => Promise<Partial<QdrantClient>>>().mockResolvedValue({
    search: vi.fn().mockResolvedValue([]),
    getCollections: vi.fn().mockResolvedValue({ collections: [] })
  })
}));

vi.mock('../lib/repository', async (importOriginal) => {
  // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
  const actual = await importOriginal() as typeof import('../lib/repository');
  return {
    ...actual, // Keep actual exports like IndexingStatusReport type
    validateGitRepository: vi.fn().mockResolvedValue(true),
    indexRepository: vi.fn().mockResolvedValue(undefined),
    getRepositoryDiff: vi.fn().mockResolvedValue('+ test\n- test2'),
    getGlobalIndexingStatus: vi.fn().mockReturnValue({
      status: 'idle',
      message: 'Mocked idle status',
      overallProgress: 0,
      lastUpdatedAt: new Date().toISOString(),
    } as IndexingStatusReport),
  };
});

vi.mock('isomorphic-git', async (importOriginal) => {
  // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
  const actual = await importOriginal() as typeof import('isomorphic-git');
  return {
    ...actual, // Keep actual exports
    default: { // Mock the default export if that's what's used
      ...(actual.default || {}), // Spread existing default export properties if any
      listFiles: vi.fn<(args: { fs: typeof fs; dir: string; gitdir: string; ref: string }) => Promise<string[]>>().mockResolvedValue(['file1.ts', 'file2.ts']),
      // Add other isomorphic-git functions if server.ts uses them directly
    },
    // If named exports from isomorphic-git are used, mock them here too
    // e.g., resolveRef: vi.fn(),
  };
});

// --- START: vi.mock for 'http' and related definitions ---
// Define shared mock function instances for the http server methods
const mockHttpServerListenFn = vi.fn((_port, listeningListenerOrHostname?: (() => void) | string, _backlog?: number, listeningListener?: () => void): httpModule.Server => {
  if (typeof listeningListenerOrHostname === 'function') {
    listeningListenerOrHostname(); // Call the listener if it's the second argument
  } else if (typeof listeningListener === 'function') {
    listeningListener(); // Call the listener if it's the fourth argument
  }
  return mockHttpServerInstance; // Return the mock server instance
}) as Mock<(...args: Parameters<httpModule.Server['listen']>) => ReturnType<httpModule.Server['listen']>>;
const mockHttpServerOnFn = vi.fn<(event: string | symbol, listener: (...args: unknown[]) => void) => httpModule.Server>();
const mockHttpServerCloseFn = vi.fn<(...args: Parameters<httpModule.Server['close']>) => ReturnType<httpModule.Server['close']>>();
const mockHttpServerAddressFn = vi.fn<(...args: Parameters<httpModule.Server['address']>) => ReturnType<httpModule.Server['address']>>();
const mockHttpServerSetTimeoutFn = vi.fn<(...args: Parameters<httpModule.Server['setTimeout']>) => ReturnType<httpModule.Server['setTimeout']>>();

// Define the mock http server instance that createServer will return
const mockHttpServerInstance = {
  listen: mockHttpServerListenFn,
  on: mockHttpServerOnFn,
  close: mockHttpServerCloseFn,
  address: mockHttpServerAddressFn,
  setTimeout: mockHttpServerSetTimeoutFn,
} as unknown as httpModule.Server; // Cast to satisfy http.Server type

vi.mock('http', async (importOriginal) => {
  // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
  const actualHttpModule = await importOriginal() as typeof httpModule; // Ensure httpModule is imported as type
  const mockHttpMethods = {
    createServer: vi.fn(() => mockHttpServerInstance),
    Server: vi.fn(() => mockHttpServerInstance) as unknown as typeof httpModule.Server, // Mock constructor
    IncomingMessage: actualHttpModule.IncomingMessage, // Preserve actual types if needed
    ServerResponse: actualHttpModule.ServerResponse,   // Preserve actual types if needed
    // Add any other http members that server.ts might use directly from the http import
  };
  return {
    ...mockHttpMethods, // Makes methods available for `import * as http from 'http'`
    default: mockHttpMethods, // This is what `import http from 'http'` will resolve to
  };
});

// Mock for axios
vi.mock('axios');

// Mock for process.exit
let mockProcessExit: MockInstance<typeof process.exit>;

// Mock for console.info
const mockConsoleInfo = vi.spyOn(console, 'info').mockImplementation(vi.fn());

// Mock for ../lib/version
vi.mock('../lib/version', () => ({
  VERSION: 'test-version-from-mock'
}));

import type { LLMProvider } from '../lib/llm-provider';
// Mock for ../lib/llm-provider
vi.mock('../lib/llm-provider', async (importOriginal) => {
  // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
  const actual = await importOriginal() as typeof import('../lib/llm-provider');
  return {
    ...actual,
    getLLMProvider: vi.fn<() => Promise<Partial<LLMProvider>>>().mockResolvedValue({
      checkConnection: vi.fn().mockResolvedValue(true),
      generateText: vi.fn().mockResolvedValue('mock llm text'),
    }),
    switchSuggestionModel: vi.fn().mockResolvedValue(true),
  };
});

// Mock fs/promises
import fs from 'fs'; // Or import type { PathOrFileDescriptor, ObjectEncodingOptions } from 'fs';
vi.mock('fs/promises', async (importOriginal) => {
  // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
  const actual = await importOriginal() as typeof import('fs/promises');
  return {
    ...actual,
    readFile: vi.fn<(path: fs.PathOrFileDescriptor, options?: fs.ObjectEncodingOptions | BufferEncoding | null) => Promise<string | Buffer>>().mockResolvedValue('mock file content'),
  };
});


describe('Server Tool Response Formatting', () => {
  beforeEach(() => {
    vi.clearAllMocks();
  });

  describe('normalizeToolParams', () => {
    it('should handle string input as query', () => {
      const result = normalizeToolParams('test query');
      expect(result).toEqual({ query: 'test query' });
    });

    it('should handle object input with query property', () => {
      const result = normalizeToolParams({ query: 'test query' });
      expect(result).toEqual({ query: 'test query' });
    });

    it('should handle object input without query property', () => {
      const input = { other: 'value' };
      const result = normalizeToolParams(input);
      expect(result).toEqual(input); // Expect the object to be returned as-is
    });

    it('should handle primitive values', () => {
      const result = normalizeToolParams(123);
      expect(result).toEqual({ query: '123' });
    });

    it('should handle null or undefined input', () => {
      expect(normalizeToolParams(null)).toEqual({ query: "" });
      expect(normalizeToolParams(undefined)).toEqual({ query: "" });
    });

    it('should handle stringified JSON object', () => {
      const input = { key: "value", num: 1 };
      const result = normalizeToolParams(JSON.stringify(input));
      expect(result).toEqual(input);
    });
  });

  describe('Tool Response Formatting', () => {
    // ... existing tests for tool response formatting ...
    // These tests are structural and don't involve server startup logic
    // so they can remain as they are.
    it('should verify search_code tool returns markdown formatted response', () => {
      // This is a structural test to ensure the response format is correct
      // The actual implementation would be tested with integration tests
      const response = `
# Search Results for: "test query"

## test/file.ts
- Last Modified: 2025-05-07T00:00:00Z
- Relevance: 0.95

### Code Snippet
\`\`\`
Test content
\`\`\`

### Summary
Test summary
`;
      
      // Verify the response contains markdown formatting elements
      expect(response).toContain('# Search Results');
      expect(response).toContain('## test/file.ts');
      expect(response).toContain('### Code Snippet');
      expect(response).toContain('```');
      expect(response).toContain('### Summary');
    });

    it('should verify generate_suggestion tool returns markdown formatted response', () => {
      const response = `
# Code Suggestion for: "test query"

## Suggestion
Test suggestion

## Context Used

### test/file.ts
- Last modified: 2025-05-07T00:00:00Z
- Relevance: 0.95

\`\`\`
Test content
\`\`\`

## Recent Changes
\`\`\`
+ test
- test2
\`\`\`
`;
      
      // Verify the response contains markdown formatting elements
      expect(response).toContain('# Code Suggestion');
      expect(response).toContain('## Suggestion');
      expect(response).toContain('## Context Used');
      expect(response).toContain('### test/file.ts');
      expect(response).toContain('```');
      expect(response).toContain('## Recent Changes');
    });

    it('should verify get_repository_context tool returns markdown formatted response', () => {
      const response = `
# Repository Context Summary

## Summary
Test suggestion

## Relevant Files

### test/file.ts
- Last modified: 2025-05-07T00:00:00Z
- Relevance: 0.95

\`\`\`
Test content
\`\`\`

## Recent Changes
\`\`\`
+ test
- test2
\`\`\`
`;
      
      // Verify the response contains markdown formatting elements
      expect(response).toContain('# Repository Context Summary');
      expect(response).toContain('## Summary');
      expect(response).toContain('## Relevant Files');
      expect(response).toContain('### test/file.ts');
      expect(response).toContain('```');
      expect(response).toContain('## Recent Changes');
    });
  });
});


// New test suite for Server Startup and Port Handling
import { ConfigService } from '../lib/config-service'; // Assuming ConfigService is the class/type of the instance
import { Logger as WinstonLogger } from 'winston';

// Type for the mocked logger instance where each method is a vi.Mock
type MockedLogger = {
  [K in keyof WinstonLogger]: WinstonLogger[K] extends (...args: infer A) => infer R
    ? Mock<(...args: A) => R> // Corrected generic usage
    : WinstonLogger[K];
};

// Type for the mocked configService instance
// Adjust properties based on what server.ts actually uses from configService
type MockedConfigService = Pick<
  ConfigService,
  | 'HTTP_PORT'
  | 'OLLAMA_HOST' // Add other properties accessed by server.ts
  | 'QDRANT_HOST'
  | 'COLLECTION_NAME'
  | 'SUGGESTION_MODEL'
  // | 'LLM_PROVIDER' // This seems to be an alias or older name, SUGGESTION_PROVIDER is used in server.ts
  | 'SUGGESTION_PROVIDER'
  | 'EMBEDDING_MODEL'
  | 'EMBEDDING_PROVIDER'
  | 'DEEPSEEK_API_KEY'
  | 'OPENAI_API_KEY'
  | 'GEMINI_API_KEY'
  | 'CLAUDE_API_KEY'
  // VERSION removed from Pick as it's not in the original ConfigService type
  | 'SUMMARIZATION_MODEL'
  | 'REFINEMENT_MODEL'
  | 'MAX_SNIPPET_LENGTH'
  // Add any other relevant properties from ConfigService
> & {
  // logger removed as it's a separate export, not a property of configService mock
  reloadConfigsFromFile: Mock<() => void>; // Corrected generic usage
  VERSION: string; // VERSION is part of the mock, but not original ConfigService
  // Add other methods from ConfigService that are mocked and used by server.ts
};

// Type for the module imported from '../lib/config-service.js'
type ConfigServiceModuleType = {
  configService: MockedConfigService;
  logger: MockedLogger;
  // If the mock factory for '../lib/config-service' spreads `actual` and `actual`
  // contains other exports that are used, they should be typed here as well.
};

describe('Server Startup and Port Handling', () => {
  // Use the new mock-aware types
  let mcs: MockedConfigService; // mcs for mockedConfigService
  let ml: MockedLogger; // ml for mockedLogger
  let mockedMcpServerConnect: MockInstance; // Typed the mock instance
  let originalNodeEnv: string | undefined;

  beforeEach(async () => {
    originalNodeEnv = process.env.NODE_ENV; // Store original NODE_ENV
    process.env.NODE_ENV = 'test'; // Set for tests
    vi.clearAllMocks(); 

    // Initialize mockProcessExit here
    mockProcessExit = vi.spyOn(process, 'exit').mockImplementation(vi.fn() as unknown as typeof process.exit);

    mockHttpServerCloseFn.mockReset();
    // Get the mocked configService and logger from the vi.mock factory
    // This ensures we are interacting with the same mocked objects that the SUT uses.
    const mockedConfigModule = await import('../lib/config-service.js') as unknown as ConfigServiceModuleType;
    // Cast to 'unknown' first, then to the mock type
    mcs = mockedConfigModule.configService as unknown as MockedConfigService; // Keep as unknown for complex mock/real hybrid
    ml = mockedConfigModule.logger as unknown as MockedLogger; // Keep as unknown for complex mock/real hybrid

    // Clear mocks using the typed instances
    // mcs and ml are already assigned from the first import.
    // No need to re-import or re-assign.

    // Clear mocks using the typed instances
    ml.info?.mockClear();
    ml.warn?.mockClear();
    ml.error?.mockClear();
    ml.debug?.mockClear();
    mcs.reloadConfigsFromFile?.mockClear();
    
    // Assign and clear the stable McpServer.connect mock
    mcpConnectStableMock.mockClear();
    mockedMcpServerConnect = mcpConnectStableMock;
    
    // Default mock for axios.get
    // eslint-disable-next-line @typescript-eslint/unbound-method
    vi.mocked(axios.get).mockResolvedValue({ status: 200, data: {} });
  });

  afterEach(() => {
    process.env.NODE_ENV = originalNodeEnv; // Restore original NODE_ENV
    // Restore any global mocks if necessary, though vi.clearAllMocks() handles most
    if (mockProcessExit) mockProcessExit.mockClear(); // mockProcessExit is defined in beforeEach
    mockConsoleInfo.mockClear();
  });

  it('should start the server and listen on the configured port if free', async () => {
    await startServer('/fake/repo');

     
     
     
    expect(mcs.reloadConfigsFromFile).toHaveBeenCalled();
    expect(http.createServer).toHaveBeenCalled();
     
    expect(mockHttpServerListenFn).toHaveBeenCalledWith(mcs.HTTP_PORT, expect.any(Function)); // Changed mockedConfigService to mcs
     
    expect(ml.info).toHaveBeenCalledWith(expect.stringContaining(`CodeCompass HTTP server listening on port ${mcs.HTTP_PORT} for status and notifications.`)); // Changed mockedLogger to ml and mockedConfigService to mcs
     
    // Removed: expect(mockedMcpServerConnect).toHaveBeenCalled();
    // This assertion is incorrect for this test, as McpServer.connect is only called
    // upon an actual MCP client initialization request to the /mcp endpoint,
    // not during general HTTP server startup.
    expect(mockProcessExit).not.toHaveBeenCalled();
  });
    // Add the new 'it' block here, starting around line 395 of your provided file content
    it('should handle EADDRINUSE, detect existing CodeCompass server, log status, and exit with 0', async () => {
      // Define the mock status for an existing server
      const existingServerPingVersion = 'existing-ping-version'; // Version obtained from ping
      const mockExistingServerStatus: IndexingStatusReport = {
        // No version property here, as IndexingStatusReport does not define it
        status: 'idle',
        message: 'Existing server idle',
        overallProgress: 100,
        lastUpdatedAt: new Date().toISOString(),
      };

      mockHttpServerListenFn.mockImplementation(
        (
          _portOrOptions?: number | string | net.ListenOptions | null,
          _hostnameOrListener?: string | (() => void),
          _backlogOrListener?: number | (() => void),
          _listeningListener?: () => void
        ): httpModule.Server => {
          const errorArgs = mockHttpServerOnFn.mock.calls.find(call => call[0] === 'error');
          if (errorArgs && typeof errorArgs[1] === 'function') {
            const errorHandler = errorArgs[1] as (err: NodeJS.ErrnoException) => void;
            const error = new Error('listen EADDRINUSE: address already in use') as NodeJS.ErrnoException;
            error.code = 'EADDRINUSE';
            errorHandler(error);
          }
          return mockHttpServerInstance;
        });

      // Mock axios.get specifically for this test
      // eslint-disable-next-line @typescript-eslint/unbound-method
      vi.mocked(axios.get).mockImplementation((url: string) => {
        if (url.endsWith('/api/ping')) {
          return Promise.resolve({ status: 200, data: { service: "CodeCompass", status: "ok", version: existingServerPingVersion } });
        }
        if (url.endsWith('/api/indexing-status')) {
          return Promise.resolve({ status: 200, data: mockExistingServerStatus });
        }
        return Promise.resolve({ status: 404, data: {} }); // Default for other calls
      });

      // Expect ServerStartupError with specific message and code
       
      await expect(startServer('/fake/repo')).rejects.toThrow(
        // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
        expect.objectContaining({
          name: "ServerStartupError",
          message: `Port ${mcs.HTTP_PORT} in use by another CodeCompass instance.`,
          exitCode: 0
        })
      );
      
       
       
       
      expect(ml.warn).toHaveBeenCalledWith(`HTTP Port ${mcs.HTTP_PORT} is already in use. Attempting to ping...`);
      // eslint-disable-next-line @typescript-eslint/unbound-method
      expect(axios.get).toHaveBeenCalledWith(`http://localhost:${mcs.HTTP_PORT}/api/ping`, { timeout: 500 });
      // eslint-disable-next-line @typescript-eslint/unbound-method
      expect(axios.get).toHaveBeenCalledWith(`http://localhost:${mcs.HTTP_PORT}/api/indexing-status`, { timeout: 1000 });
      
      expect(mockConsoleInfo).toHaveBeenCalledWith(expect.stringContaining(`--- Status of existing CodeCompass instance on port ${mcs.HTTP_PORT} ---`));
       
      expect(mockConsoleInfo).toHaveBeenCalledWith(expect.stringContaining(`Version: ${existingServerPingVersion}`)); // Use version from ping
       
      expect(mockConsoleInfo).toHaveBeenCalledWith(expect.stringContaining(`Status: ${mockExistingServerStatus.status}`));
       
      expect(mockConsoleInfo).toHaveBeenCalledWith(expect.stringContaining(`Progress: ${mockExistingServerStatus.overallProgress}%`));
      
       
      expect(ml.info).toHaveBeenCalledWith("Current instance will exit as another CodeCompass server is already running.");
      // mockProcessExit is not directly called by startServer's main catch in test mode anymore
      expect(mockedMcpServerConnect).not.toHaveBeenCalled();
    });

  it('should handle EADDRINUSE, detect a non-CodeCompass server, log error, and exit with 1', async () => {
    mockHttpServerListenFn.mockImplementation(
      (
        _portOrOptions?: number | string | net.ListenOptions | null,
        _hostnameOrListener?: string | (() => void),
        _backlogOrListener?: number | (() => void),
        _listeningListener?: () => void
      ): httpModule.Server => {
        const errorArgs = mockHttpServerOnFn.mock.calls.find(call => call[0] === 'error');
        if (errorArgs && typeof errorArgs[1] === 'function') {
          const errorHandler = errorArgs[1] as (err: NodeJS.ErrnoException) => void;
          const error = new Error('listen EADDRINUSE: address already in use') as NodeJS.ErrnoException;
          error.code = 'EADDRINUSE';
          errorHandler(error);
        }
        return mockHttpServerInstance;
      });

    // Mock axios.get for /api/ping
    // eslint-disable-next-line @typescript-eslint/unbound-method
    vi.mocked(axios.get).mockImplementation((url: string) => {
      if (url.endsWith('/api/ping')) { // Ping returns non-CodeCompass response or error
        return Promise.resolve({ status: 200, data: { service: "OtherService" } });
      }
      return Promise.resolve({ status: 404, data: {} });
    });
     
    await expect(startServer('/fake/repo')).rejects.toThrow(
      // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
      expect.objectContaining({
        name: "ServerStartupError",
        message: `Port ${mcs.HTTP_PORT} in use by non-CodeCompass server.`,
        exitCode: 1
      })
    );

    // Verify the specific error log calls in order
    expect(ml.error).toHaveBeenNthCalledWith(1, expect.stringContaining(`Port ${mcs.HTTP_PORT} is in use by non-CodeCompass server. Response: {"service":"OtherService"}`));
    expect(ml.error).toHaveBeenNthCalledWith(2, expect.stringContaining('Please free the port or configure a different one'));
    expect(ml.error).toHaveBeenNthCalledWith(3, "Failed to start CodeCompass", expect.objectContaining({ message: `Port ${mcs.HTTP_PORT} in use by non-CodeCompass server.` }));
    
    expect(mockedMcpServerConnect).not.toHaveBeenCalled();
  });

  it('should handle EADDRINUSE, ping fails (e.g. ECONNREFUSED), log error, and exit with 1', async () => {
    mockHttpServerListenFn.mockImplementation(
      (
        _portOrOptions?: number | string | net.ListenOptions | null,
        _hostnameOrListener?: string | (() => void),
        _backlogOrListener?: number | (() => void),
        _listeningListener?: () => void
      ): httpModule.Server => {
        const errorArgs = mockHttpServerOnFn.mock.calls.find(call => call[0] === 'error');
        if (errorArgs && typeof errorArgs[1] === 'function') {
          const errorHandler = errorArgs[1] as (err: NodeJS.ErrnoException) => void;
          const error = new Error('listen EADDRINUSE') as NodeJS.ErrnoException;
          error.code = 'EADDRINUSE';
          errorHandler(error); // Simulate EADDRINUSE
        }
        return mockHttpServerInstance;
      }
    );

    const pingError = new Error('Connection refused') as NodeJS.ErrnoException;
    pingError.code = 'ECONNREFUSED';
    // eslint-disable-next-line @typescript-eslint/unbound-method
    vi.mocked(axios.get).mockImplementation((url: string) => {
      if (url.endsWith('/api/ping')) {
        return Promise.reject(pingError);
      }
      return Promise.resolve({ status: 404, data: {} });
    });
    
     
    await expect(startServer('/fake/repo')).rejects.toThrow(
      // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
      expect.objectContaining({
        name: "ServerStartupError",
        message: `Port ${mcs.HTTP_PORT} in use or ping failed.`,
        exitCode: 1
      })
    );

     
     
     
    expect(ml.error).toHaveBeenCalledWith(expect.stringContaining(`Port ${mcs.HTTP_PORT} is in use by an unknown service or the existing CodeCompass server is unresponsive to pings.`));
     
    expect(ml.error).toHaveBeenCalledWith(expect.stringContaining('Ping error details: Error: Connection refused'));
     
    expect(ml.error).toHaveBeenCalledWith(expect.stringContaining('Please free the port or configure a different one'));
    // Add this new expectation for the log from the main catch block
     
    expect(ml.error).toHaveBeenCalledWith("Failed to start CodeCompass", expect.objectContaining({ message: `Port ${mcs.HTTP_PORT} in use or ping failed.` }));
    expect(mockedMcpServerConnect).not.toHaveBeenCalled(); // MCP server should not connect
  });

  it('should handle EADDRINUSE, ping OK, but /api/indexing-status fails, log error, and exit with 1', async () => {
    mockHttpServerListenFn.mockImplementation(() => {
      const errorArgs = mockHttpServerOnFn.mock.calls.find(call => call[0] === 'error');
      if (errorArgs && typeof errorArgs[1] === 'function') {
        const errorHandler = errorArgs[1] as (err: NodeJS.ErrnoException) => void;
        const error = new Error('listen EADDRINUSE') as NodeJS.ErrnoException;
        error.code = 'EADDRINUSE';
        errorHandler(error); // Simulate EADDRINUSE
      }
      return mockHttpServerInstance;
    });

    // Mock axios.get for /api/ping
    // eslint-disable-next-line @typescript-eslint/unbound-method
    vi.mocked(axios.get).mockImplementation((url: string) => {
      if (url.endsWith('/api/ping')) {
        return Promise.resolve({ status: 200, data: { service: "CodeCompass", status: "ok", version: "test-version" } }); // Ping success
      }
      if (url.endsWith('/api/indexing-status')) {
        return Promise.reject(new Error('Failed to fetch status')); // Status fetch fails
      }
      // This was incorrect, should be the response for axios.get
      return Promise.resolve({ status: 404, data: {} });
    });

     
    await expect(startServer('/fake/repo')).rejects.toThrow(
      // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
      expect.objectContaining({
        name: "ServerStartupError",
        // The message might vary slightly based on the exact point of failure in the EADDRINUSE logic for status fetch
        // For example, if it's the catch block after statusError:
        message: `Port ${mcs.HTTP_PORT} in use, status fetch error.`, 
        exitCode: 1
      })
    );
        
         
         
     
    expect(ml.error).toHaveBeenCalledWith(expect.stringContaining('Error fetching status from existing CodeCompass server'));
    expect(mockedMcpServerConnect).not.toHaveBeenCalled();
  });

  it('should handle non-EADDRINUSE errors on HTTP server and exit with 1', async () => {
    const otherError = new Error('Some other server error') as NodeJS.ErrnoException;
    otherError.code = 'EACCES'; // Example of another error code

    mockHttpServerListenFn.mockImplementation(() => {
      // Simulate listen failure by invoking the 'error' handler
      const errorArgs = mockHttpServerOnFn.mock.calls.find(call => call[0] === 'error');
      if (errorArgs && typeof errorArgs[1] === 'function') {
        const errorHandler = errorArgs[1] as (err: NodeJS.ErrnoException) => void;
        errorHandler(otherError);
      }
      return mockHttpServerInstance;
    });
    
    // We need to ensure listen is called to trigger the 'on' setup
    
     
    await expect(startServer('/fake/repo')).rejects.toThrow(
      // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
      expect.objectContaining({
        name: "ServerStartupError",
        message: `HTTP server error: ${otherError.message}`,
        exitCode: 1
      })
    );
    
    // Check that the 'on' handler was attached
         
         
     
    expect(mockHttpServerOnFn).toHaveBeenCalledWith('error', expect.any(Function));
        
    // Check for the specific error log for non-EADDRINUSE
         
    expect(ml.error).toHaveBeenCalledWith(`Failed to start HTTP server on port ${mcs.HTTP_PORT}: ${otherError.message}`);
     
    expect(mockedMcpServerConnect).not.toHaveBeenCalled();
  });
});
