import { SpamShieldService, ReputationResult } from '../services/spamshield.service';
import { RuleEngine, RuleMatch } from './rule-engine';
import {
  DEFAULT_REPUTATION_WEIGHT,
  DEFAULT_RULE_WEIGHT,
  DEFAULT_BEHAVIORAL_WEIGHT,
  DEFAULT_USER_HISTORY_WEIGHT,
  DEFAULT_BLOCK_THRESHOLD,
  DEFAULT_FLAG_THRESHOLD,
  DEFAULT_EVALUATION_TIMEOUT,
  DEFAULT_FALLBACK_DECISION,
  DEFAULT_FALLBACK_ON_TIMEOUT,
  SHORT_CALL_SCORE,
  SHORT_SMS_SCORE,
  SHORT_CONTENT_SCORE,
  URGENT_KEYWORD_SCORE,
} from '../constants/decision-engine.constants';

export interface CallMetadata {
  callId: string;
  startTime: Date;
  duration?: number;
  direction: 'inbound' | 'outbound';
  callType?: 'voice' | 'video' | 'sms';
  carrierInfo?: Record<string, any>;
}

export interface SmsContent {
  messageId: string;
  body: string;
  timestamp: Date;
  direction: 'inbound' | 'outbound';
}

export interface UserSpamHistory {
  phoneNumberHash: string;
  spamCount: number;
  hamCount: number;
  lastSpamReportedAt?: Date;
  userPreference?: 'block' | 'flag' | 'allow';
}

export interface DecisionContext {
  phoneNumber: string;
  phoneNumberHash?: string;
  callMetadata?: CallMetadata;
  smsContent?: SmsContent;
  cachedReputation: ReputationResult;
  ruleMatches: RuleMatch[];
  userHistory?: UserSpamHistory;
  requestId?: string;
}

export interface DecisionResult {
  decision: 'BLOCK' | 'FLAG' | 'ALLOW';
  confidence: number;
  reasons: string[];
  fallbackDecision: 'BLOCK' | 'FLAG' | 'ALLOW';
  scoring: {
    reputationScore: number;
    ruleScore: number;
    behavioralScore: number;
    userHistoryScore: number;
    totalScore: number;
  };
  executedAt: Date;
  requestId?: string;
}

export interface DecisionEngineConfig {
  // Scoring weights
  reputationWeight?: number;
  ruleWeight?: number;
  behavioralWeight?: number;
  userHistoryWeight?: number;
  
  // Thresholds
  blockThreshold?: number;
  flagThreshold?: number;
  
  // Timeouts
  evaluationTimeout?: number;
  
  // Fallback behavior
  fallbackOnTimeout?: boolean;
  fallbackDecision?: 'BLOCK' | 'FLAG' | 'ALLOW';
}

// Configuration defaults exported from constants module

export class DecisionEngine {
  private readonly config: Required<DecisionEngineConfig>;
  private readonly reputationService: SpamShieldService;
  private readonly ruleEngine: RuleEngine;

  constructor(
    reputationService: SpamShieldService,
    ruleEngine: RuleEngine,
    config?: DecisionEngineConfig
  ) {
    this.config = {
      reputationWeight: config?.reputationWeight ?? DEFAULT_REPUTATION_WEIGHT,
      ruleWeight: config?.ruleWeight ?? DEFAULT_RULE_WEIGHT,
      behavioralWeight: config?.behavioralWeight ?? DEFAULT_BEHAVIORAL_WEIGHT,
      userHistoryWeight: config?.userHistoryWeight ?? DEFAULT_USER_HISTORY_WEIGHT,
      blockThreshold: config?.blockThreshold ?? DEFAULT_BLOCK_THRESHOLD,
      flagThreshold: config?.flagThreshold ?? DEFAULT_FLAG_THRESHOLD,
      evaluationTimeout: config?.evaluationTimeout ?? DEFAULT_EVALUATION_TIMEOUT,
      fallbackOnTimeout: config?.fallbackOnTimeout ?? DEFAULT_FALLBACK_ON_TIMEOUT,
      fallbackDecision: config?.fallbackDecision ?? DEFAULT_FALLBACK_DECISION,
    };
    this.reputationService = reputationService;
    this.ruleEngine = ruleEngine;
  }

  async evaluate(context: DecisionContext): Promise<DecisionResult> {
    const startTime = Date.now();
    const reqId = context.requestId ?? 'unknown';
    const fallback: DecisionResult = {
      decision: this.config.fallbackDecision,
      confidence: 0.5,
      reasons: ['Fallback decision due to evaluation timeout'],
      fallbackDecision: this.config.fallbackDecision,
      scoring: {
        reputationScore: 0.5,
        ruleScore: 0.5,
        behavioralScore: 0.5,
        userHistoryScore: 0.5,
        totalScore: 0.5,
      },
      executedAt: new Date(),
      requestId: reqId,
    };

    const evaluation = (async () => {
      const [reputationScore, ruleScore, behavioralScore, userHistoryScore] = await Promise.all([
        this.calculateReputationScore(context.cachedReputation),
        this.calculateRuleScore(context.ruleMatches),
        this.calculateBehavioralScore(context),
        this.calculateUserHistoryScore(context.userHistory),
      ]);

      const totalScore =
        reputationScore * this.config.reputationWeight +
        ruleScore * this.config.ruleWeight +
        behavioralScore * this.config.behavioralWeight +
        userHistoryScore * this.config.userHistoryWeight;

      const decision = this.applyThresholds(totalScore);
      const reasons = this.collectReasons(
        reputationScore, ruleScore, behavioralScore, userHistoryScore, context.ruleMatches
      );

      return {
        decision,
        confidence: totalScore,
        reasons,
        fallbackDecision: this.config.fallbackDecision,
        scoring: {
          reputationScore,
          ruleScore,
          behavioralScore,
          userHistoryScore,
          totalScore,
        },
        executedAt: new Date(),
        requestId: reqId,
      };
    })();

    try {
      const result = await Promise.race([
        evaluation,
        new Promise<DecisionResult>((resolve) => {
          setTimeout(() => {
            console.log(`[DecisionEngine] [${reqId}] Evaluation timeout after ${this.config.evaluationTimeout}ms`);
            resolve(fallback);
          }, this.config.evaluationTimeout);
        }),
      ]);

      return result;
    } catch (error) {
      console.error(`[DecisionEngine] [${reqId}] Evaluation error:`, error);

      if (this.config.fallbackOnTimeout) {
        return { ...fallback, reasons: ['Fallback decision due to evaluation error'] };
      }

      throw error;
    }
  }

  private async calculateReputationScore(reputation: ReputationResult): Promise<number> {
    return reputation.score;
  }

  private async calculateRuleScore(ruleMatches: RuleMatch[]): Promise<number> {
    if (ruleMatches.length === 0) {
      return 0;
    }

    const totalScore = ruleMatches.reduce((sum, match) => sum + match.score, 0);
    return Math.min(totalScore, 1.0);
  }

  private async calculateBehavioralScore(context: DecisionContext): Promise<number> {
    let score = 0;

    if (context.callMetadata) {
      const { callMetadata } = context;
      
      if (callMetadata.duration && callMetadata.duration < 5) {
        score += SHORT_CALL_SCORE;
      }
      
      if (callMetadata.callType === 'sms') {
        score += SHORT_SMS_SCORE;
      }
    }

    if (context.smsContent) {
      const { smsContent } = context;
      
      if (smsContent.body.length < 10) {
        score += SHORT_CONTENT_SCORE;
      }
      
      if (/\b(URGENT|ACT NOW|LIMITED)\b/i.test(smsContent.body)) {
        score += URGENT_KEYWORD_SCORE;
      }
    }

    return Math.min(score, 1.0);
  }

  private async calculateUserHistoryScore(userHistory?: UserSpamHistory): Promise<number> {
    if (!userHistory) {
      return 0.5;
    }

    const totalReports = userHistory.spamCount + userHistory.hamCount;
    if (totalReports === 0) {
      return 0.5;
    }

    const spamRatio = userHistory.spamCount / totalReports;
    
    if (userHistory.userPreference) {
      switch (userHistory.userPreference) {
        case 'block':
          return 1.0;
        case 'flag':
          return 0.6;
        case 'allow':
          return 0.2;
      }
    }

    return spamRatio;
  }

  private applyThresholds(score: number): 'BLOCK' | 'FLAG' | 'ALLOW' {
    if (score >= this.config.blockThreshold) {
      return 'BLOCK';
    }
    if (score >= this.config.flagThreshold) {
      return 'FLAG';
    }
    return 'ALLOW';
  }

  private collectReasons(
    reputationScore: number,
    ruleScore: number,
    behavioralScore: number,
    userHistoryScore: number,
    ruleMatches: RuleMatch[]
  ): string[] {
    const reasons: string[] = [];

    if (reputationScore > 0.8) {
      reasons.push(`High reputation spam score: ${reputationScore.toFixed(2)}`);
    }

    if (ruleMatches.length > 0) {
      reasons.push(`Matched ${ruleMatches.length} spam rule(s)`);
      ruleMatches.forEach(match => {
        reasons.push(`  - ${match.ruleName} (${match.score.toFixed(2)})`);
      });
    }

    if (behavioralScore > 0.5) {
      reasons.push(`Suspicious behavioral pattern detected`);
    }

    if (userHistoryScore > 0.7) {
      reasons.push(`User history indicates high spam probability`);
    }

    if (reasons.length === 0) {
      reasons.push('No spam indicators detected');
    }

    return reasons;
  }

  getConfig(): Required<DecisionEngineConfig> {
    return { ...this.config };
  }

  updateConfig(config: Partial<DecisionEngineConfig>): void {
    this.config.reputationWeight = config.reputationWeight ?? this.config.reputationWeight;
    this.config.ruleWeight = config.ruleWeight ?? this.config.ruleWeight;
    this.config.behavioralWeight = config.behavioralWeight ?? this.config.behavioralWeight;
    this.config.userHistoryWeight = config.userHistoryWeight ?? this.config.userHistoryWeight;
    this.config.blockThreshold = config.blockThreshold ?? this.config.blockThreshold;
    this.config.flagThreshold = config.flagThreshold ?? this.config.flagThreshold;
    this.config.evaluationTimeout = config.evaluationTimeout ?? this.config.evaluationTimeout;
    this.config.fallbackOnTimeout = config.fallbackOnTimeout ?? this.config.fallbackOnTimeout;
    this.config.fallbackDecision = config.fallbackDecision ?? this.config.fallbackDecision;
  }
}
