import logger from '@utils/logger';
import { getAIProviderConfig } from './configReader.service';

// Provider-specific interfaces
interface AIProvider {
  generateText(prompt: string): Promise<string>;
  isAvailable(): boolean;
  getProviderName(): string;
}

// Claude Provider
class ClaudeProvider implements AIProvider {
  private client: any = null;
  private model: string;

  constructor(apiKey?: string, model?: string) {
    // Allow model override via parameter, environment variable, or default
    // Current models (November 2025):
    // - claude-sonnet-4-20250514 (default - latest Claude Sonnet 4)
    // Priority: 1. Provided model parameter, 2. Environment variable, 3. Default
    this.model = model || process.env.CLAUDE_MODEL || 'claude-sonnet-4-20250514';
    
    try {
      // Priority: 1. Provided key, 2. Environment variable
      const key = apiKey || process.env.CLAUDE_API_KEY || process.env.ANTHROPIC_API_KEY;
      
      if (!key || key.trim() === '') {
        return; // Silently skip if no key available
      }

      // Dynamic import to avoid hard dependency
      const Anthropic = require('@anthropic-ai/sdk');
      this.client = new Anthropic({ apiKey: key });
      logger.info(`[AI Service] ✅ Claude provider initialized with model: ${this.model}`);
    } catch (error: any) {
      // Handle missing package gracefully
      if (error.code === 'MODULE_NOT_FOUND') {
        logger.warn('[AI Service] Claude SDK not installed. Run: npm install @anthropic-ai/sdk');
      } else {
        logger.error('[AI Service] Failed to initialize Claude:', error.message);
      }
    }
  }

  async generateText(prompt: string): Promise<string> {
    if (!this.client) throw new Error('Claude client not initialized');
    
    logger.info(`[AI Service] Generating with Claude model: ${this.model}`);
    
    const response = await this.client.messages.create({
      model: this.model,
      max_tokens: 2048, // Increased for longer conclusions
      temperature: 0.3,
      messages: [{ role: 'user', content: prompt }]
    });

    const content = response.content[0];
    return content.type === 'text' ? content.text : '';
  }

  isAvailable(): boolean {
    return this.client !== null;
  }

  getProviderName(): string {
    return 'Claude (Anthropic)';
  }
}

// OpenAI Provider
class OpenAIProvider implements AIProvider {
  private client: any = null;
  private model: string;

  constructor(apiKey?: string, model?: string) {
    // Allow model override via parameter, environment variable, or default
    // Current models (November 2025):
    // - gpt-4o (default - latest GPT-4 Optimized)
    // Priority: 1. Provided model parameter, 2. Environment variable, 3. Default
    this.model = model || process.env.OPENAI_MODEL || 'gpt-4o';
    
    try {
      // Priority: 1. Provided key, 2. Environment variable
      const key = apiKey || process.env.OPENAI_API_KEY;
      
      if (!key || key.trim() === '') {
        return; // Silently skip if no key available
      }

      const OpenAI = require('openai');
      this.client = new OpenAI({ apiKey: key });
      logger.info(`[AI Service] ✅ OpenAI provider initialized with model: ${this.model}`);
    } catch (error: any) {
      // Handle missing package gracefully
      if (error.code === 'MODULE_NOT_FOUND') {
        logger.warn('[AI Service] OpenAI SDK not installed. Run: npm install openai');
      } else {
        logger.error('[AI Service] Failed to initialize OpenAI:', error.message);
      }
    }
  }

  async generateText(prompt: string): Promise<string> {
    if (!this.client) throw new Error('OpenAI client not initialized');
    
    logger.info(`[AI Service] Generating with OpenAI model: ${this.model}`);
    
    const response = await this.client.chat.completions.create({
      model: this.model,
      messages: [{ role: 'user', content: prompt }],
      max_tokens: 1024,
      temperature: 0.3
    });

    return response.choices[0]?.message?.content || '';
  }

  isAvailable(): boolean {
    return this.client !== null;
  }

  getProviderName(): string {
    return 'OpenAI (GPT-4)';
  }
}

// Gemini Provider (Google)
class GeminiProvider implements AIProvider {
  private client: any = null;
  private model: string;

  constructor(apiKey?: string, model?: string) {
    // Allow model override via parameter, environment variable, or default
    // Current models (November 2025):
    // - gemini-2.0-flash-lite (default - latest Gemini Flash Lite)
    // Priority: 1. Provided model parameter, 2. Environment variable, 3. Default
    this.model = model || process.env.GEMINI_MODEL || 'gemini-2.0-flash-lite';
    
    try {
      // Priority: 1. Provided key, 2. Environment variable
      const key = apiKey || process.env.GEMINI_API_KEY || process.env.GOOGLE_AI_API_KEY;
      
      if (!key || key.trim() === '') {
        return; // Silently skip if no key available
      }

      const { GoogleGenerativeAI } = require('@google/generative-ai');
      this.client = new GoogleGenerativeAI(key);
      logger.info(`[AI Service] ✅ Gemini provider initialized with model: ${this.model}`);
    } catch (error: any) {
      // Handle missing package gracefully
      if (error.code === 'MODULE_NOT_FOUND') {
        logger.warn('[AI Service] Gemini SDK not installed. Run: npm install @google/generative-ai');
      } else {
        logger.error('[AI Service] Failed to initialize Gemini:', error.message);
      }
    }
  }

  async generateText(prompt: string): Promise<string> {
    if (!this.client) throw new Error('Gemini client not initialized');
    
    logger.info(`[AI Service] Generating with Gemini model: ${this.model}`);
    
    const model = this.client.getGenerativeModel({ model: this.model });
    const result = await model.generateContent(prompt);
    const response = await result.response;
    return response.text();
  }

  isAvailable(): boolean {
    return this.client !== null;
  }

  getProviderName(): string {
    return 'Gemini (Google)';
  }
}

class AIService {
  private provider: AIProvider | null = null;
  private providerName: string = 'None';
  private isInitialized: boolean = false;

  constructor() {
    // Initialization happens asynchronously
    this.initialize();
  }

  /**
   * Initialize AI provider from database configuration
   */
  async initialize(): Promise<void> {
    try {
      // Read AI configuration from database (with env fallback)
      const config = await getAIProviderConfig();
      
      if (!config.enabled) {
        logger.warn('[AI Service] AI features disabled in admin configuration');
        return;
      }

      const preferredProvider = config.provider.toLowerCase();
      logger.info(`[AI Service] Preferred provider from config: ${preferredProvider}`);
      
      // Try to initialize the preferred provider first
      let initialized = false;
      
      switch (preferredProvider) {
        case 'openai':
        case 'gpt':
          initialized = this.tryProvider(new OpenAIProvider(config.openaiKey, config.openaiModel));
          break;
        case 'gemini':
        case 'google':
          initialized = this.tryProvider(new GeminiProvider(config.geminiKey, config.geminiModel));
          break;
        case 'claude':
        case 'anthropic':
        default:
          initialized = this.tryProvider(new ClaudeProvider(config.claudeKey, config.claudeModel));
          break;
      }
      
      // Fallback: Try other providers if preferred one failed
      if (!initialized) {
        logger.warn('[AI Service] Preferred provider unavailable. Trying fallbacks...');
        
        const fallbackProviders = [
          new ClaudeProvider(config.claudeKey, config.claudeModel),
          new OpenAIProvider(config.openaiKey, config.openaiModel),
          new GeminiProvider(config.geminiKey, config.geminiModel)
        ];
        
        for (const provider of fallbackProviders) {
          if (this.tryProvider(provider)) {
            logger.info(`[AI Service] ✅ Using fallback provider: ${this.providerName}`);
            break;
          }
        }
      }
      
      if (!this.provider) {
        logger.warn('[AI Service] ⚠️ No AI provider available. AI features will be disabled.');
        logger.warn('[AI Service] To enable AI: Configure API keys in admin panel or set environment variables.');
        logger.warn('[AI Service] Supported providers: Claude (CLAUDE_API_KEY), OpenAI (OPENAI_API_KEY), Gemini (GEMINI_API_KEY)');
      }
      
      this.isInitialized = true;
    } catch (error) {
      logger.error('[AI Service] Failed to initialize from config:', error);
      // Fallback to environment variables
      try {
        this.initializeFromEnv();
      } catch (envError) {
        logger.error('[AI Service] Environment fallback also failed:', envError);
        this.isInitialized = true; // Mark as initialized even if failed
      }
    }
  }

  /**
   * Fallback initialization from environment variables
   */
  private initializeFromEnv(): void {
    try {
      const preferredProvider = (process.env.AI_PROVIDER || 'claude').toLowerCase();
      
      logger.info(`[AI Service] Using environment variable configuration`);
      
      switch (preferredProvider) {
        case 'openai':
        case 'gpt':
          this.tryProvider(new OpenAIProvider(undefined, process.env.OPENAI_MODEL));
          break;
        case 'gemini':
        case 'google':
          this.tryProvider(new GeminiProvider(undefined, process.env.GEMINI_MODEL));
          break;
        case 'claude':
        case 'anthropic':
        default:
          this.tryProvider(new ClaudeProvider(undefined, process.env.CLAUDE_MODEL));
          break;
      }
      
      if (!this.provider) {
        logger.warn('[AI Service] ⚠️ No provider available from environment variables either.');
      }
      
      this.isInitialized = true;
    } catch (error) {
      logger.error('[AI Service] Environment initialization failed:', error);
      this.isInitialized = true; // Still mark as initialized to prevent infinite loops
    }
  }

  /**
   * Reinitialize AI provider (call after admin updates config)
   */
  async reinitialize(): Promise<void> {
    logger.info('[AI Service] Reinitializing AI provider from updated configuration...');
    this.provider = null;
    this.providerName = 'None';
    this.isInitialized = false;
    await this.initialize();
  }

  private tryProvider(provider: AIProvider): boolean {
    if (provider.isAvailable()) {
      this.provider = provider;
      this.providerName = provider.getProviderName();
      logger.info(`[AI Service] ✅ Active provider: ${this.providerName}`);
      return true;
    }
    return false;
  }

  /**
   * Get current AI provider name
   */
  getProviderName(): string {
    return this.providerName;
  }

  /**
   * Generate conclusion remark for a workflow request
   * @param context - All relevant data for generating the conclusion
   * @returns AI-generated conclusion remark
   */
  async generateConclusionRemark(context: {
    requestTitle: string;
    requestDescription: string;
    requestNumber: string;
    priority: string;
    approvalFlow: Array<{
      levelNumber: number;
      approverName: string;
      status: string;
      comments?: string;
      actionDate?: string;
      tatHours?: number;
      elapsedHours?: number;
    }>;
    workNotes: Array<{
      userName: string;
      message: string;
      createdAt: string;
    }>;
    documents: Array<{
      fileName: string;
      uploadedBy: string;
      uploadedAt: string;
    }>;
    activities: Array<{
      type: string;
      action: string;
      details: string;
      timestamp: string;
    }>;
    }): Promise<{ remark: string; confidence: number; keyPoints: string[]; provider: string }> {
    // Ensure initialization is complete
    if (!this.isInitialized) {
      logger.warn('[AI Service] Not yet initialized, attempting initialization...');
      await this.initialize();
    }

    if (!this.provider) {
      logger.error('[AI Service] No AI provider available');
      throw new Error('AI features are currently unavailable. Please configure an AI provider (Claude, OpenAI, or Gemini) in the admin panel, or write the conclusion manually.');
    }

    try {
      // Build context prompt with max length from config
      const prompt = await this.buildConclusionPrompt(context);

      logger.info(`[AI Service] Generating conclusion for request ${context.requestNumber} using ${this.providerName}...`);

      // Use provider's generateText method
      let remarkText = await this.provider.generateText(prompt);

      // Get max length from config for validation
      const { getConfigValue } = require('./configReader.service');
      const maxLengthStr = await getConfigValue('AI_MAX_REMARK_LENGTH', '2000');
      const maxLength = parseInt(maxLengthStr || '2000', 10);

      // Validate and trim if exceeds max length
      if (remarkText.length > maxLength) {
        logger.warn(`[AI Service] Generated remark exceeds max length (${remarkText.length} > ${maxLength}), trimming...`);
        remarkText = remarkText.substring(0, maxLength - 3) + '...'; // Trim with ellipsis
      }

      // Extract key points (look for bullet points or numbered items)
      const keyPoints = this.extractKeyPoints(remarkText);

      // Calculate confidence based on response quality (simple heuristic)
      const confidence = this.calculateConfidence(remarkText, context);

      logger.info(`[AI Service] ✅ Generated conclusion (${remarkText.length}/${maxLength} chars, ${keyPoints.length} key points) via ${this.providerName}`);

      return {
        remark: remarkText,
        confidence: confidence,
        keyPoints: keyPoints,
        provider: this.providerName
      };
    } catch (error: any) {
      logger.error('[AI Service] Failed to generate conclusion:', error);
      throw new Error(`AI generation failed (${this.providerName}): ${error.message}`);
    }
  }

  /**
   * Build the prompt for Claude to generate a professional conclusion remark
   */
  private async buildConclusionPrompt(context: any): Promise<string> {
    const {
      requestTitle,
      requestDescription,
      requestNumber,
      priority,
      approvalFlow,
      workNotes,
      documents,
      activities,
      rejectionReason,
      rejectedBy
    } = context;

    // Get max remark length from admin configuration
    const { getConfigValue } = require('./configReader.service');
    const maxLengthStr = await getConfigValue('AI_MAX_REMARK_LENGTH', '2000');
    const maxLength = parseInt(maxLengthStr || '2000', 10);
    const targetWordCount = Math.floor(maxLength / 6); // Approximate words (avg 6 chars per word)
    
    logger.info(`[AI Service] Using max remark length: ${maxLength} characters (≈${targetWordCount} words) from admin config`);

    // Check if this is a rejected request
    const isRejected = rejectionReason || rejectedBy || approvalFlow.some((a: any) => a.status === 'REJECTED');

    // Helper function to determine TAT risk status
    const getTATRiskStatus = (tatPercentage: number): string => {
      if (tatPercentage < 50) return 'ON_TRACK';
      if (tatPercentage < 75) return 'AT_RISK';
      if (tatPercentage < 100) return 'CRITICAL';
      return 'BREACHED';
    };

    // Summarize approvals with TAT risk information
    const approvalSummary = approvalFlow
      .filter((a: any) => a.status === 'APPROVED' || a.status === 'REJECTED')
      .map((a: any) => {
        const tatPercentage = a.tatPercentageUsed !== undefined && a.tatPercentageUsed !== null 
          ? Number(a.tatPercentageUsed) 
          : (a.elapsedHours && a.tatHours ? (Number(a.elapsedHours) / Number(a.tatHours)) * 100 : 0);
        const riskStatus = getTATRiskStatus(tatPercentage);
        const tatInfo = a.elapsedHours && a.tatHours 
          ? ` (completed in ${a.elapsedHours.toFixed(1)}h of ${a.tatHours}h TAT, ${tatPercentage.toFixed(1)}% used)`
          : '';
        const riskInfo = riskStatus !== 'ON_TRACK' ? ` [${riskStatus}]` : '';
        return `- Level ${a.levelNumber}: ${a.approverName} ${a.status}${tatInfo}${riskInfo}${a.comments ? `\n  Comment: "${a.comments}"` : ''}`;
      })
      .join('\n');

    // Summarize work notes (limit to important ones)
    const workNoteSummary = workNotes
      .slice(-10) // Last 10 work notes
      .map((wn: any) => `- ${wn.userName}: "${wn.message.substring(0, 150)}${wn.message.length > 150 ? '...' : ''}"`)
      .join('\n');

    // Summarize documents
    const documentSummary = documents
      .map((d: any) => `- ${d.fileName} (by ${d.uploadedBy})`)
      .join('\n');

    // Build rejection context if applicable
    const rejectionContext = isRejected 
      ? `\n**Rejection Details:**\n- Rejected by: ${rejectedBy || 'Approver'}\n- Rejection reason: ${rejectionReason || 'Not specified'}`
      : '';

    const prompt = `You are writing a closure summary for a workflow request at Royal Enfield. Write a practical, realistic conclusion that an employee would write when closing a request.

**Request:**
${requestNumber} - ${requestTitle}
Description: ${requestDescription}
Priority: ${priority}

**What Happened:**
${approvalSummary || 'No approvals recorded'}${rejectionContext}

**Discussions (if any):**
${workNoteSummary || 'No work notes'}

**Documents:**
${documentSummary || 'No documents'}

**YOUR TASK:**
Write a brief, professional conclusion (approximately ${targetWordCount} words, max ${maxLength} characters) that:
${isRejected 
  ? `- Summarizes what was requested and explains that it was rejected
- Mentions who rejected it and the rejection reason
- Notes the outcome and any learnings or next steps
- Mentions if any approval levels were AT_RISK, CRITICAL, or BREACHED (if applicable)
- Uses clear, factual language without time-specific references
- Is suitable for permanent archiving and future reference
- Sounds natural and human-written (not AI-generated)
- Maintains a professional and constructive tone even for rejections`
  : `- Summarizes what was requested and the final decision
- Mentions who approved it and any key comments
- Mentions if any approval levels were AT_RISK, CRITICAL, or BREACHED (if applicable)
- Notes the outcome and next steps (if applicable)
- Uses clear, factual language without time-specific references
- Is suitable for permanent archiving and future reference
- Sounds natural and human-written (not AI-generated)`}

**IMPORTANT:**
- Be concise and direct
- MUST stay within ${maxLength} characters limit
- No time-specific words like "today", "now", "currently", "recently"
- No corporate jargon or buzzwords
- No emojis or excessive formatting
- Write like a professional documenting a completed process
- Focus on facts: what was requested, who ${isRejected ? 'rejected' : 'approved'}, what was decided
- Use past tense for completed actions

Write the conclusion now (remember: max ${maxLength} characters):`;

    return prompt;
  }

  /**
   * Extract key points from the AI-generated remark
   */
  private extractKeyPoints(remark: string): string[] {
    const keyPoints: string[] = [];
    
    // Look for bullet points (-, •, *) or numbered items (1., 2., etc.)
    const lines = remark.split('\n');
    
    for (const line of lines) {
      const trimmed = line.trim();
      
      // Match bullet points
      if (trimmed.match(/^[-•*]\s+(.+)$/)) {
        const point = trimmed.replace(/^[-•*]\s+/, '');
        if (point.length > 10) { // Ignore very short lines
          keyPoints.push(point);
        }
      }
      
      // Match numbered items
      if (trimmed.match(/^\d+\.\s+(.+)$/)) {
        const point = trimmed.replace(/^\d+\.\s+/, '');
        if (point.length > 10) {
          keyPoints.push(point);
        }
      }
    }
    
    // If no bullet points found, extract first few sentences
    if (keyPoints.length === 0) {
      const sentences = remark.split(/[.!?]+/).filter(s => s.trim().length > 20);
      keyPoints.push(...sentences.slice(0, 3).map(s => s.trim()));
    }
    
    return keyPoints.slice(0, 5); // Max 5 key points
  }

  /**
   * Calculate confidence score based on response quality
   */
  private calculateConfidence(remark: string, context: any): number {
    let score = 0.6; // Base score (slightly higher for new prompt)
    
    // Check if remark has good length (100-400 chars - more realistic)
    if (remark.length >= 100 && remark.length <= 400) {
      score += 0.2;
    }
    
    // Check if remark mentions key elements
    if (remark.toLowerCase().includes('approv')) {
      score += 0.1;
    }
    
    // Check if remark is not too generic
    if (remark.length > 80 && !remark.toLowerCase().includes('lorem ipsum')) {
      score += 0.1;
    }
    
    return Math.min(1.0, score);
  }

  /**
   * Check if AI service is available
   */
  isAvailable(): boolean {
    return this.provider !== null;
  }
}

export const aiService = new AIService();

