import { prisma, SpamFeedback } from '@shieldai/db';
import { spamShieldEnv, SpamDecision, spamFeatureFlags, defaultScores, metadataLimits } from './spamshield.config';
import { createHash } from 'crypto';
import { spamAuditLogger, hashPhoneNumber } from './spamshield.audit-logger';

// Number reputation service (Hiya API integration)
export class NumberReputationService {
  /**
   * Check number reputation using Hiya API
   */
  async checkReputation(phoneNumber: string): Promise<{
    isSpam: boolean;
    confidence: number;
    spamType?: string;
    reportCount: number;
  }> {
    try {
      // Only enable if feature flag is set
      if (!spamFeatureFlags.enableNumberReputation) {
        return {
          isSpam: false,
          confidence: 0.0,
          reportCount: 0,
        };
      }

      // TODO: Integrate with Hiya API
      // const response = await fetch(`${spamShieldEnv.HIYA_API_URL}/lookup`, {
      //   headers: { 'X-API-Key': spamShieldEnv.HIYA_API_KEY },
      //   method: 'POST',
      //   body: JSON.stringify({ phone: phoneNumber }),
      // });
      
      // Simulated response for now
      return {
        isSpam: false,
        confidence: defaultScores.defaultReputationLowConfidence,
        spamType: undefined,
        reportCount: 0,
      };
    } catch (error) {
      console.error('Error checking number reputation:', error);
      return {
        isSpam: false,
        confidence: defaultScores.defaultReputationConfidence,
        reportCount: 0,
      };
    }
  }

  /**
   * Check number against multiple reputation sources
   */
  async checkMultiSource(phoneNumber: string): Promise<{
    hiya: { isSpam: boolean; confidence: number };
    truecaller: { isSpam: boolean; confidence: number } | null;
    combinedScore: number;
  }> {
    // Only enable if feature flag is set
    if (!spamFeatureFlags.enableMultipleSources) {
      return {
        hiya: { isSpam: false, confidence: defaultScores.defaultReputationConfidence },
        truecaller: null,
        combinedScore: defaultScores.defaultSpamScore,
      };
    }

    const hiyaResult = await this.checkReputation(phoneNumber);
    
    let truecallerResult: { isSpam: boolean; confidence: number } | null = null;
    if (spamShieldEnv.TRUECALLER_API_KEY) {
      // TODO: Integrate Truecaller
      truecallerResult = {
        isSpam: false,
        confidence: defaultScores.defaultReputationConfidence,
      };
    }

    // Weighted average: Hiya 70%, Truecaller 30%
    const combinedScore = hiyaResult.confidence * defaultScores.hiyaWeightInCombinedScore + 
                          (truecallerResult?.confidence ?? defaultScores.defaultReputationConfidence) * defaultScores.truecallerWeightInCombinedScore;

    return {
      hiya: { isSpam: hiyaResult.isSpam, confidence: hiyaResult.confidence },
      truecaller: truecallerResult,
      combinedScore,
    };
  }
}

// SMS content classifier (BERT-based)
export class SMSClassifierService {
  private model: any = null; // BERT model placeholder
  private _initPromise: Promise<void> | null = null;

  /**
   * Initialize the BERT model (thread-safe via promise deduplication)
   */
  async initialize(): Promise<void> {
    // TODO: Load BERT model from path
    // this.model = await loadBERTModel(spamShieldEnv.BERT_MODEL_PATH);
    console.log('SMS classifier initialized');
  }

  /**
   * Ensures model is initialized before use. Concurrent callers
   * await the same initialization promise to avoid race conditions.
   */
  private async ensureInitialized(): Promise<void> {
    if (this._initPromise) {
      return this._initPromise;
    }
    this._initPromise = (async () => {
      if (this.model) {
        return;
      }
      await this.initialize();
    })();
    return this._initPromise;
  }

  /**
   * Classify SMS text as spam or ham
   */
  async classify(
    smsText: string,
    phoneNumber?: string
  ): Promise<{
    isSpam: boolean;
    confidence: number;
    spamFeatures: string[];
  }> {
    // Only enable if feature flag is set
    if (!spamFeatureFlags.enableMLClassifier) {
      // Return basic feature-based classification
      const features = this.extractFeatures(smsText);
      const confidence = this.calculateConfidence(features);
      const isSpam = confidence >= spamShieldEnv.SPAM_THRESHOLD_AUTO_BLOCK;

      spamAuditLogger.logClassification({
        type: 'sms',
        phoneNumberHash: phoneNumber ? hashPhoneNumber(phoneNumber) : 'unknown',
        decision: isSpam ? 'spam' : 'ham',
        confidence,
        reasons: features,
        featureFlags: { enableMLClassifier: spamFeatureFlags.enableMLClassifier },
      });

      return {
        isSpam,
        confidence,
        spamFeatures: features,
      };
    }

    await this.ensureInitialized();

    // Extract features
    const features = this.extractFeatures(smsText);

    // TODO: Run through BERT model
    // const prediction = await this.model.predict(smsText);

    // Simulated prediction
    const confidence = this.calculateConfidence(features);
    const isSpam = confidence >= spamShieldEnv.SPAM_THRESHOLD_AUTO_BLOCK;

    spamAuditLogger.logClassification({
      type: 'sms',
      phoneNumberHash: phoneNumber ? hashPhoneNumber(phoneNumber) : 'unknown',
      decision: isSpam ? 'spam' : 'ham',
      confidence,
      reasons: features,
      featureFlags: { enableMLClassifier: spamFeatureFlags.enableMLClassifier },
    });

    return {
      isSpam,
      confidence,
      spamFeatures: features,
    };
  }

  private extractFeatures(text: string): string[] {
    const features: string[] = [];
    const lowerText = text.toLowerCase();

    // URL presence
    if (/(http|www)\./i.test(text)) {
      features.push('url_present');
    }

    // Emoji density
    const emojiCount = (text.match(/[\p{Emoji}]/gu) || []).length;
    if (emojiCount / text.length > 0.1) {
      features.push('high_emoji_density');
    }

    // Urgency keywords
    const urgencyWords = ['now', 'urgent', 'limited', 'act fast', 'today'];
    if (urgencyWords.some(word => lowerText.includes(word))) {
      features.push('urgency_keyword');
    }

    // Excessive capitalization
    if (/[A-Z]{3,}/.test(text)) {
      features.push('excessive_caps');
    }

    return features;
  }

  private calculateConfidence(features: string[]): number {
    const baseConfidence = defaultScores.defaultBaseConfidence;
    const featureWeights: Record<string, number> = {
      url_present: defaultScores.featureWeights.urlPresent,
      high_emoji_density: defaultScores.featureWeights.highEmojiDensity,
      urgency_keyword: defaultScores.featureWeights.urgencyKeyword,
      excessive_caps: defaultScores.featureWeights.excessiveCaps,
    };

    return Math.min(defaultScores.defaultMaxConfidence, baseConfidence + 
      features.reduce((sum, f) => sum + (featureWeights[f] || 0), 0));
  }
}

// Call analysis service
export class CallAnalysisService {
  /**
   * Analyze incoming call for spam indicators
   */
  async analyzeCall(callData: {
    phoneNumber: string;
    duration?: number;
    callTime: Date;
    isVoip?: boolean;
  }): Promise<{
    decision: SpamDecision;
    confidence: number;
    reasons: string[];
  }> {
    const reasons: string[] = [];
    let spamScore = defaultScores.defaultSpamScore;

    // Number reputation check - only if feature flag enabled
    if (spamFeatureFlags.enableBehavioralAnalysis) {
      const reputationService = new NumberReputationService();
      const reputation = await reputationService.checkMultiSource(callData.phoneNumber);
      
      if (reputation.combinedScore > defaultScores.highReputationThreshold) {
        spamScore += reputation.combinedScore * defaultScores.reputationWeightInCombinedScore;
        reasons.push('high_spam_reputation');
      }
    }

    // Behavioral analysis - only if feature flag enabled
    if (spamFeatureFlags.enableBehavioralAnalysis) {
      if (callData.duration && callData.duration < 10) {
        spamScore += defaultScores.shortDurationScore;
        reasons.push('short_duration');
      }

      if (callData.isVoip) {
        spamScore += defaultScores.voipScore;
        reasons.push('voip_number');
      }

      // Time-of-day anomaly (simplified)
      const hour = callData.callTime.getHours();
      if (hour < 6 || hour > 22) {
        spamScore += defaultScores.unusualHoursScore;
        reasons.push('unusual_hours');
      }
    }

    // Determine decision
    let decision: SpamDecision;
    if (spamScore >= spamShieldEnv.SPAM_THRESHOLD_AUTO_BLOCK) {
      decision = SpamDecision.BLOCK;
    } else if (spamScore >= spamShieldEnv.SPAM_THRESHOLD_FLAG) {
      decision = SpamDecision.FLAG;
    } else {
      decision = SpamDecision.ALLOW;
    }

    spamAuditLogger.logClassification({
      type: 'call',
      phoneNumberHash: hashPhoneNumber(callData.phoneNumber),
      decision: decision.toLowerCase() as 'block' | 'flag' | 'allow',
      confidence: spamScore,
      reasons,
      featureFlags: {
        enableBehavioralAnalysis: spamFeatureFlags.enableBehavioralAnalysis,
        enableNumberReputation: spamFeatureFlags.enableNumberReputation,
      },
      metadata: {
        duration: callData.duration,
        isVoip: callData.isVoip,
        callTime: callData.callTime.toISOString(),
      },
    });

    return {
      decision,
      confidence: spamScore,
      reasons,
    };
  }
}

// User feedback service
export class SpamFeedbackService {
  /**
   * Validate metadata size against defined limits
   */
  private validateMetadata(metadata?: Record<string, any>): {
    isValid: boolean;
    trimmedMetadata?: Record<string, any>;
    reasons?: string[];
  } {
    if (!metadata) {
      return { isValid: true };
    }

    const reasons: string[] = [];
    let trimmedMetadata: Record<string, any> = metadata;

    // Check number of keys
    const keyCount = Object.keys(metadata).length;
    if (keyCount > metadataLimits.maxMetadataKeys) {
      reasons.push(`Metadata has ${keyCount} keys, exceeding limit of ${metadataLimits.maxMetadataKeys}`);
      trimmedMetadata = Object.entries(metadata).slice(0, metadataLimits.maxMetadataKeys);
    }

    // Check total JSON size
    const jsonSize = JSON.stringify(metadata).length;
    if (jsonSize > metadataLimits.maxMetadataSizeBytes) {
      reasons.push(`Metadata size ${jsonSize} bytes exceeds limit of ${metadataLimits.maxMetadataSizeBytes} bytes`);
      
      // Truncate long values
      trimmedMetadata = Object.fromEntries(
        Object.entries(metadata).map(([key, value]) => {
          const valueStr = String(value);
          if (valueStr.length > metadataLimits.maxMetadataValueSizeBytes) {
            return [key, valueStr.slice(0, metadataLimits.maxMetadataValueSizeBytes)];
          }
          return [key, value];
        })
      );
    }

    return {
      isValid: reasons.length === 0,
      trimmedMetadata,
      reasons: reasons.length > 0 ? reasons : undefined,
    };
  }

  /**
   * Record user feedback on spam detection
   */
  async recordFeedback(
    userId: string,
    phoneNumber: string,
    isSpam: boolean,
    confidence?: number,
    metadata?: Record<string, any>
  ): Promise<SpamFeedback> {
    // Defensive null checks for required fields
    if (!userId || typeof userId !== 'string' || userId.trim().length === 0) {
      throw new Error('Feedback: userId is required');
    }

    if (!phoneNumber || typeof phoneNumber !== 'string' || phoneNumber.trim().length === 0) {
      throw new Error('Feedback: phoneNumber is required');
    }

    if (typeof isSpam !== 'boolean') {
      throw new Error('Feedback: isSpam must be a boolean');
    }

    // Validate confidence range if provided
    const validatedConfidence = confidence !== undefined && confidence !== null
      ? (Number.isFinite(confidence) && confidence >= 0 && confidence <= 1 ? confidence : undefined)
      : undefined;

    // Treat null metadata as undefined
    const effectiveMetadata = metadata !== null ? metadata : undefined;
    const validation = this.validateMetadata(effectiveMetadata);
    const validatedMetadata = validation.trimmedMetadata;

    // Only enable if feature flag is set
    if (!spamFeatureFlags.enableCommunityIntelligence) {
      // Return a mock feedback for development
      return {
        id: `mock_${Date.now()}`,
        userId,
        phoneNumber,
        phoneNumberHash: this.hashPhoneNumber(phoneNumber),
        isSpam,
        confidence: validatedConfidence,
        feedbackType: 'user_confirmation' as const,
        metadata: validatedMetadata,
        createdAt: new Date(),
        updatedAt: new Date(),
      };
    }

    const phoneNumberHash = this.hashPhoneNumber(phoneNumber);

    const feedback = await prisma.spamFeedback.create({
      data: {
        userId,
        phoneNumber,
        phoneNumberHash,
        isSpam,
        confidence: validatedConfidence,
        feedbackType: 'user_confirmation',
        metadata: validatedMetadata,
      },
    });

    return feedback;
  }

  /**
   * Get spam history for a user
   */
  async getSpamHistory(
    userId: string,
    options?: {
      limit?: number;
      isSpam?: boolean;
      startDate?: Date;
    }
  ): Promise<SpamFeedback[]> {
    return prisma.spamFeedback.findMany({
      where: {
        userId,
        ...(options?.isSpam !== undefined && { isSpam: options.isSpam }),
        ...(options?.startDate && { createdAt: { gte: options.startDate } }),
      },
      orderBy: { createdAt: 'desc' },
      take: options?.limit ?? 100,
    });
  }

  /**
   * Get statistics for a user
   */
  async getStatistics(userId: string): Promise<{
    totalAnalyses: number;
    spamCount: number;
    hamCount: number;
    spamPercentage: number;
  }> {
    const [total, spam] = await Promise.all([
      prisma.spamFeedback.count({ where: { userId } }),
      prisma.spamFeedback.count({ where: { userId, isSpam: true } }),
    ]);

    return {
      totalAnalyses: total,
      spamCount: spam,
      hamCount: total - spam,
      spamPercentage: total > 0 ? (spam / total) * 100 : 0,
    };
  }

  private hashPhoneNumber(phoneNumber: string): string {
    // SHA-256 hash for phone number fingerprinting
    const hash = createHash('sha256').update(phoneNumber).digest('hex');
    return `sha256_${hash}`;
  }
}

// Export instances
export const numberReputationService = new NumberReputationService();
export const smsClassifierService = new SMSClassifierService();
export const callAnalysisService = new CallAnalysisService();
export const spamFeedbackService = new SpamFeedbackService();
