import { z } from 'zod';
import { defineTool, type ToolResult } from './types.js';
import { EmbeddingService, type SearchResult } from '../../embeddings/index.js';

// Singleton instance for the embedding service
let embeddingService: EmbeddingService | null = null;

/**
 * Get or create the embedding service
 */
function getEmbeddingService(repoRoot: string): EmbeddingService {
  if (!embeddingService || embeddingService['repoRoot'] !== repoRoot) {
    embeddingService = new EmbeddingService({ repoRoot });
  }
  return embeddingService;
}

interface SemanticSearchOutput {
  results: Array<{
    text: string;
    score: number;
    file?: string;
    lines?: { start: number; end: number };
  }>;
  totalResults: number;
}

/**
 * Semantic search tool using embeddings
 */
export const semanticSearchTool = defineTool({
  name: 'semantic_search',
  description: `Search for code or text semantically using natural language queries.
Unlike grep which matches exact patterns, semantic search finds conceptually similar code.

Use this for:
- Finding code that implements a concept (e.g., "authentication logic")
- Finding similar implementations (e.g., "error handling patterns")
- Searching with natural language (e.g., "how are API requests made")

Note: Requires an embedding index. Use index_repository first if search returns no results.`,
  category: 'search',
  schema: z.object({
    query: z.string().describe('Natural language search query'),
    top_k: z.number().optional().default(10).describe('Maximum number of results'),
    threshold: z
      .number()
      .optional()
      .default(0.5)
      .describe('Minimum similarity score (0-1)'),
  }),

  async execute(
    input,
    context
  ): Promise<ToolResult<SemanticSearchOutput>> {
    const service = getEmbeddingService(context.repoRoot);

    // Check if index exists, try to load it
    if (service.getIndexSize() === 0) {
      const loaded = await service.loadIndex();
      if (!loaded) {
        return {
          success: false,
          error: 'No embedding index found. Run index_repository first.',
          suggestions: ['Use index_repository tool to create the index'],
        };
      }
    }

    try {
      const results = await service.search(
        input.query,
        input.top_k ?? 10,
        input.threshold ?? 0.5
      );

      return {
        success: true,
        data: {
          results: results.map((r) => ({
            text: r.text.slice(0, 500), // Truncate long results
            score: Math.round(r.score * 1000) / 1000,
            file: r.metadata?.file as string | undefined,
            lines: r.metadata?.start !== undefined
              ? {
                  start: r.metadata.start as number,
                  end: r.metadata.end as number,
                }
              : undefined,
          })),
          totalResults: results.length,
        },
      };
    } catch (err) {
      return {
        success: false,
        error: `Semantic search failed: ${err}`,
      };
    }
  },
});

interface IndexOutput {
  filesIndexed: number;
  chunksIndexed: number;
}

/**
 * Index repository for semantic search
 */
export const indexRepositoryTool = defineTool({
  name: 'index_repository',
  description: `Create an embedding index of the repository for semantic search.

This indexes source code files and creates embeddings for semantic search.
The index is cached and can be reused across sessions.

Supported file types: .ts, .tsx, .js, .jsx, .py, .go, .rs, .java, .md`,
  category: 'search',
  schema: z.object({
    extensions: z
      .array(z.string())
      .optional()
      .describe('File extensions to index (e.g., [".ts", ".py"])'),
    force: z.boolean().optional().describe('Force re-indexing even if index exists'),
  }),

  async execute(
    input,
    context
  ): Promise<ToolResult<IndexOutput>> {
    const service = getEmbeddingService(context.repoRoot);

    // Check for existing index
    if (!input.force && service.getIndexSize() === 0) {
      const loaded = await service.loadIndex();
      if (loaded) {
        return {
          success: true,
          data: {
            filesIndexed: 0,
            chunksIndexed: service.getIndexSize(),
          },
          metadata: { cached: true },
        };
      }
    }

    try {
      context.logger.info('Starting repository indexing...');

      const result = await service.indexRepository({
        extensions: input.extensions,
      });

      // Save index to disk
      await service.saveIndex();

      context.logger.info(
        { filesIndexed: result.filesIndexed, chunksIndexed: result.chunksIndexed },
        'Repository indexed'
      );

      return {
        success: true,
        data: result,
      };
    } catch (err) {
      return {
        success: false,
        error: `Indexing failed: ${err}`,
      };
    }
  },
});
