// src/utils/toolSelector.ts
import { aiService } from './aiService.js';
import { embeddingsService, type SimilarityResult } from './embeddings.js';
import { confidenceScoring } from './confidenceScoring.js';
import { JSONParser } from './jsonUtils.js';
import { getPrompt } from '../config/prompts.js';
import 'dotenv/config';

export interface ToolSelectionConfig {
  maxSelectedItems: number;
  embeddingCandidates: number;
  similarityThreshold: number;
  embeddingSelectionLimit: number;
  embeddingConceptsLimit: number;
  noEmbeddingsToolLimit: number;
  noEmbeddingsConceptLimit: number;
  embeddingsMinTools: number;
  embeddingsMaxReductionRatio: number;
  methodSelectionRatio: number;
  softwareSelectionRatio: number;
}

export interface SelectionContext {
  userQuery: string;
  mode: string;
  embeddingsSimilarities: Map<string, number>;
  seenToolNames: Set<string>;
  selectedTools?: Array<{
    tool: any;
    phase: string;
    priority: string;
    justification?: string;
    taskRelevance?: number;
    limitations?: string[];
  }>;
}

export interface ToolSelectionResult {
  selectedTools: any[];
  selectedConcepts: any[];
  confidence: number;
}

class ToolSelector {
  private config: ToolSelectionConfig;

  constructor() {
    this.config = {
      maxSelectedItems: this.getEnvInt('AI_MAX_SELECTED_ITEMS', 25),
      embeddingCandidates: this.getEnvInt('AI_EMBEDDING_CANDIDATES', 50),
      similarityThreshold: this.getEnvFloat('AI_SIMILARITY_THRESHOLD', 0.3),
      embeddingSelectionLimit: this.getEnvInt('AI_EMBEDDING_SELECTION_LIMIT', 30),
      embeddingConceptsLimit: this.getEnvInt('AI_EMBEDDING_CONCEPTS_LIMIT', 15),
      noEmbeddingsToolLimit: this.getEnvInt('AI_NO_EMBEDDINGS_TOOL_LIMIT', 25),
      noEmbeddingsConceptLimit: this.getEnvInt('AI_NO_EMBEDDINGS_CONCEPT_LIMIT', 10),
      embeddingsMinTools: this.getEnvInt('AI_EMBEDDINGS_MIN_TOOLS', 8),
      embeddingsMaxReductionRatio: this.getEnvFloat('AI_EMBEDDINGS_MAX_REDUCTION_RATIO', 0.75),
      methodSelectionRatio: this.getEnvFloat('AI_METHOD_SELECTION_RATIO', 0.4),
      softwareSelectionRatio: this.getEnvFloat('AI_SOFTWARE_SELECTION_RATIO', 0.5)
    };

    console.log('[TOOL-SELECTOR] Initialized with config:', this.config);
  }

  private getEnvInt(key: string, defaultValue: number): number {
    const value = process.env[key];
    return value ? parseInt(value, 10) : defaultValue;
  }

  private getEnvFloat(key: string, defaultValue: number): number {
    const value = process.env[key];
    return value ? parseFloat(value) : defaultValue;
  }

  async getIntelligentCandidates(
    userQuery: string,
    toolsData: any,
    mode: string,
    context: SelectionContext
  ): Promise<{
    tools: any[];
    concepts: any[];
    domains: any[];
    phases: any[];
    'domain-agnostic-software': any[];
  }> {
    console.log('[TOOL-SELECTOR] Getting intelligent candidates for query');
    
    let candidateTools: any[] = [];
    let candidateConcepts: any[] = [];
    
    context.embeddingsSimilarities.clear();
    
    try {
      await embeddingsService.waitForInitialization();
    } catch (error) {
      console.error('[TOOL-SELECTOR] Embeddings initialization failed:', error);
    }
    
    console.log('[TOOL-SELECTOR] Using embeddings for candidate selection');
    
    const embeddingsSearchStart = Date.now();
    
    const similarItems = await embeddingsService.findSimilar(
      userQuery,
      this.config.embeddingCandidates,
      this.config.similarityThreshold
    ) as SimilarityResult[];
    
    console.log('[TOOL-SELECTOR] Embeddings found', similarItems.length, 'similar items');
    
    const { auditService } = await import('./auditService.js');
    const { getDataVersion } = await import('./dataService.js');
    
    const toolsDataHash = getDataVersion() || 'unknown';
    
    auditService.addEmbeddingsSearch(
      userQuery,
      similarItems,
      this.config.similarityThreshold,
      embeddingsSearchStart,
      {
        toolsDataHash: toolsDataHash,
        selectionPhase: 'initial-candidate-selection',
        candidateLimit: this.config.embeddingCandidates,
        mode: mode,
        reasoning: `Initiale semantische Suche für ${mode}-Modus - Reduzierung der ${toolsData.tools.length} verfügbaren Tools auf ${similarItems.length} relevante Kandidaten`
      }
    );
    
    similarItems.forEach(item => {
      context.embeddingsSimilarities.set(item.name, item.similarity);
    });
    
    const toolsMap = new Map(toolsData.tools.map((tool: any) => [tool.name, tool]));
    const conceptsMap = new Map(toolsData.concepts.map((concept: any) => [concept.name, concept]));
    
    const similarTools = similarItems
      .filter((item: any) => item.type === 'tool')
      .map((item: any) => toolsMap.get(item.name))
      .filter((tool: any): tool is NonNullable<any> => tool !== undefined && tool !== null);
    
    const similarConcepts = similarItems
      .filter((item: any) => item.type === 'concept')
      .map((item: any) => conceptsMap.get(item.name))
      .filter((concept: any): concept is NonNullable<any> => concept !== undefined && concept !== null);
    
    const totalAvailableTools = toolsData.tools.length;
    const reductionRatio = similarTools.length / totalAvailableTools;
    
    if (similarTools.length >= this.config.embeddingsMinTools && reductionRatio <= this.config.embeddingsMaxReductionRatio) {
      candidateTools = similarTools;
      candidateConcepts = similarConcepts;
      
      console.log('[TOOL-SELECTOR] Using embeddings filtering:', totalAvailableTools, '→', similarTools.length, 'tools');
    } else {
      console.log('[TOOL-SELECTOR] Embeddings filtering insufficient, using full dataset');
      candidateTools = toolsData.tools;
      candidateConcepts = toolsData.concepts;
    }

    const selection = await this.performAISelection(
      userQuery,
      candidateTools,
      candidateConcepts,
      mode,
      context
    );
    
    return {
      tools: selection.selectedTools,
      concepts: selection.selectedConcepts,
      domains: toolsData.domains,
      phases: toolsData.phases,
      'domain-agnostic-software': toolsData['domain-agnostic-software']
    };
  }

  private async performAISelection(
    userQuery: string,
    candidateTools: any[],
    candidateConcepts: any[],
    mode: string,
    context: SelectionContext
  ): Promise<ToolSelectionResult> {
    console.log('[TOOL-SELECTOR] Performing AI selection');

    const candidateMethods = candidateTools.filter((tool: any) => tool && tool.type === 'method');
    const candidateSoftware = candidateTools.filter((tool: any) => tool && tool.type === 'software');

    console.log('[TOOL-SELECTOR] Candidates:', candidateMethods.length, 'methods,', candidateSoftware.length, 'software,', candidateConcepts.length, 'concepts');

    const methodsWithFullData = candidateMethods.map(this.createToolData);
    const softwareWithFullData = candidateSoftware.map(this.createToolData);
    const conceptsWithFullData = candidateConcepts.map(this.createConceptData);

    const maxTools = Math.min(this.config.embeddingSelectionLimit, this.config.noEmbeddingsToolLimit);
    const maxConcepts = Math.min(this.config.embeddingConceptsLimit, this.config.noEmbeddingsConceptLimit);
    const methodLimit = Math.ceil(maxTools * this.config.methodSelectionRatio);
    const softwareLimit = Math.floor(maxTools * this.config.softwareSelectionRatio);

    const toolsToSend: any[] = [
      ...methodsWithFullData.slice(0, methodLimit),
      ...softwareWithFullData.slice(0, softwareLimit),
    ];

    const remainingCapacity = maxTools - toolsToSend.length;
    if (remainingCapacity > 0) {
      const extraMethods = methodsWithFullData.slice(methodLimit, methodLimit + remainingCapacity);
      const extraSoftware = softwareWithFullData.slice(softwareLimit, softwareLimit + (remainingCapacity - extraMethods.length));
      toolsToSend.push(...extraMethods, ...extraSoftware);
    }

    const conceptsToSend = conceptsWithFullData.slice(0, maxConcepts);

    const basePrompt = getPrompt('toolSelection', mode, userQuery, this.config.maxSelectedItems);
    const prompt = getPrompt('toolSelectionWithData', basePrompt, toolsToSend, conceptsToSend);

    console.log('[TOOL-SELECTOR] Sending to AI:',
      toolsToSend.filter((t: any) => t.type === 'method').length, 'methods,',
      toolsToSend.filter((t: any) => t.type === 'software').length, 'software,',
      conceptsToSend.length, 'concepts'
    );

    try {
      const response = await aiService.callAI(prompt);
      const result = JSONParser.safeParseJSON(response.content, null);

      if (!result || !Array.isArray(result.selectedTools) || !Array.isArray(result.selectedConcepts)) {
        console.error('[TOOL-SELECTOR] AI selection returned invalid structure');
        throw new Error('AI selection failed to return valid tool and concept selection');
      }

      const totalSelected = result.selectedTools.length + result.selectedConcepts.length;
      if (totalSelected === 0) {
        throw new Error('AI selection returned empty selection');
      }

      const toolsMap = new Map(candidateTools.map((tool: any) => [tool.name, tool]));
      const conceptsMap = new Map(candidateConcepts.map((concept: any) => [concept.name, concept]));

      const selectedTools = result.selectedTools
        .map((name: string) => toolsMap.get(name))
        .filter((tool: any): tool is NonNullable<any> => tool !== undefined && tool !== null);

      const selectedConcepts = result.selectedConcepts
        .map((name: string) => conceptsMap.get(name))
        .filter((concept: any): concept is NonNullable<any> => concept !== undefined && concept !== null);

      const selectedMethods = selectedTools.filter((t: any) => t && t.type === 'method');
      const selectedSoftware = selectedTools.filter((t: any) => t && t.type === 'software');

      console.log('[TOOL-SELECTOR] AI selected:', selectedMethods.length, 'methods,', selectedSoftware.length, 'software,', selectedConcepts.length, 'concepts');

      const confidence = confidenceScoring.calculateSelectionConfidence(
        result,
        candidateTools.length + candidateConcepts.length
      );

      return { selectedTools, selectedConcepts, confidence };
    } catch (error) {
      console.error('[TOOL-SELECTOR] AI selection failed:', error);
      throw error;
    }
  }

  async selectToolsForPhase(
    userQuery: string,
    phase: any,
    availableTools: any[],
    context: SelectionContext
  ): Promise<Array<{
    toolName: string;
    taskRelevance: number;
    justification: string;
    limitations: string[];
  }>> {
    console.log('[TOOL-SELECTOR] Selecting tools for phase:', phase.id);
    
    if (availableTools.length === 0) {
      console.log('[TOOL-SELECTOR] No tools available for phase:', phase.id);
      return [];
    }

    const prompt = getPrompt('phaseToolSelection', userQuery, phase, availableTools);
    
    try {
      const response = await aiService.callMicroTaskAI(prompt);
      const selections = JSONParser.safeParseJSON(response.content, []);
      
      if (Array.isArray(selections)) {
        const validSelections = selections.filter((sel: any) => {
          const matchingTool = availableTools.find((tool: any) => tool && tool.name === sel.toolName);
          if (!matchingTool) {
            console.warn('[TOOL-SELECTOR] Invalid tool selection for phase:', phase.id, sel.toolName);
          }
          return !!matchingTool;
        });
        
        console.log('[TOOL-SELECTOR] Valid selections for phase:', phase.id, validSelections.length);
        return validSelections;
      }
      
      return [];
      
    } catch (error) {
      console.error('[TOOL-SELECTOR] Phase tool selection failed:', error);
      return [];
    }
  }

  private createToolData = (tool: any) => ({
    name: tool.name,
    type: tool.type,
    description: tool.description,
    domains: tool.domains,
    phases: tool.phases,
    platforms: tool.platforms || [],
    tags: tool.tags || [],
    skillLevel: tool.skillLevel,
    license: tool.license,
    accessType: tool.accessType,
    projectUrl: tool.projectUrl,
    knowledgebase: tool.knowledgebase,
    related_concepts: tool.related_concepts || [],
    related_software: tool.related_software || []
  });

  private createConceptData = (concept: any) => ({
    name: concept.name,
    type: 'concept',
    description: concept.description,
    domains: concept.domains,
    phases: concept.phases,
    tags: concept.tags || [],
    skillLevel: concept.skillLevel,
    related_concepts: concept.related_concepts || [],
    related_software: concept.related_software || []
  });

  getConfig(): ToolSelectionConfig {
    return { ...this.config };
  }
}

export const toolSelector = new ToolSelector();