import { PrismaClient, SpamRule } from '@prisma/client';
import { generateRequestId } from '@shieldai/types';
import { validateRegexPattern, RegexValidationError } from '../utils/regex-validation';

export interface CompiledRule {
  rule: SpamRule;
  compiledPattern: RegExp;
  compiledCaseInsensitive?: RegExp;
}

export interface RuleMatch {
  ruleId: string;
  ruleName: string;
  pattern: string;
  score: number;
  priority: 'high' | 'medium' | 'low';
  matchedAt: Date;
}

export interface RuleEngineConfig {
  loadIntervalMs?: number;
  enableCache?: boolean;
  cacheTtlMs?: number;
}

const DEFAULT_CONFIG: Required<RuleEngineConfig> = {
  loadIntervalMs: 60000,
  enableCache: true,
  cacheTtlMs: 300000,
};

export class RuleEngine {
  private readonly config: Required<RuleEngineConfig>;
  private numberPatternRules: CompiledRule[] = [];
  private behavioralRules: CompiledRule[] = [];
  private contentRules: CompiledRule[] = [];
  private allRules: CompiledRule[] = [];
  private lastLoadTime: Date | null = null;
  private readonly prisma: PrismaClient;

  constructor(prisma?: PrismaClient, config?: RuleEngineConfig) {
    this.prisma = prisma ?? new PrismaClient() as PrismaClient;
    this.config = { ...DEFAULT_CONFIG, ...config };
  }

  async loadActiveRules(): Promise<void> {
    const now = new Date();

    if (this.config.enableCache && this.lastLoadTime) {
      const elapsed = now.getTime() - this.lastLoadTime.getTime();
      if (elapsed < this.config.loadIntervalMs) {
        return;
      }
    }

    const rules = await this.prisma.spamRule.findMany({
      where: { isActive: true },
      orderBy: { priority: 'desc' },
    });

    const compiledRules: CompiledRule[] = [];
    for (const rule of rules) {
      try {
        validateRegexPattern(rule.pattern);
        const compiledPattern = new RegExp(rule.pattern);
        const compiledCaseInsensitive = new RegExp(rule.pattern, 'i');
        compiledRules.push({
          rule,
          compiledPattern,
          compiledCaseInsensitive,
        });
      } catch (error) {
        if (error instanceof RegexValidationError) {
          console.warn(`[RuleEngine] [req:${generateRequestId()}] Rule "${rule.name}" (${rule.id}) ReDoS risk: ${error.reason}, skipping`);
        } else {
          console.error(`[RuleEngine] [req:${generateRequestId()}] Unexpected error validating rule "${rule.name}" (${rule.id}):`, error);
        }
      }
    }

    this.allRules = compiledRules;
    this.numberPatternRules = compiledRules.filter(r => (r.rule as any).category === 'number_pattern');
    this.behavioralRules = compiledRules.filter(r => (r.rule as any).category === 'behavioral');
    this.contentRules = compiledRules.filter(r => (r.rule as any).category === 'content');
    this.lastLoadTime = now;
  }

  async evaluate(phoneNumber: string): Promise<RuleMatch[]> {
    if (this.allRules.length === 0) {
      await this.loadActiveRules();
    }

    const matches: RuleMatch[] = [];

    for (const compiled of this.allRules) {
      try {
        if (compiled.compiledPattern.test(phoneNumber)) {
          matches.push({
            ruleId: compiled.rule.id,
            ruleName: compiled.rule.name,
            pattern: compiled.rule.pattern,
            score: (compiled.rule as any).score,
            priority: (compiled.rule as any).priority as 'high' | 'medium' | 'low',
            matchedAt: new Date(),
          });
        }
      } catch (error) {
        console.error(`[RuleEngine] [req:${generateRequestId()}] Evaluation error for rule ${compiled.rule.id}:`, error);
      }
    }

    return matches.sort((a, b) => b.score - a.score);
  }

  async evaluateSms(smsBody: string): Promise<RuleMatch[]> {
    if (this.contentRules.length === 0) {
      await this.loadActiveRules();
    }

    const matches: RuleMatch[] = [];

    for (const compiled of this.contentRules) {
      try {
        if (compiled.compiledCaseInsensitive!.test(smsBody)) {
          matches.push({
            ruleId: compiled.rule.id,
            ruleName: compiled.rule.name,
            pattern: compiled.rule.pattern,
            score: (compiled.rule as any).score,
            priority: (compiled.rule as any).priority as 'high' | 'medium' | 'low',
            matchedAt: new Date(),
          });
        }
      } catch (error) {
        console.error(`[RuleEngine] [req:${generateRequestId()}] SMS evaluation error for rule ${compiled.rule.id}:`, error);
      }
    }

    return matches.sort((a, b) => b.score - a.score);
  }

  getNumberPatternRules(): SpamRule[] {
    return this.numberPatternRules.map(r => r.rule);
  }

  getBehavioralRules(): SpamRule[] {
    return this.behavioralRules.map(r => r.rule);
  }

  getContentRules(): SpamRule[] {
    return this.contentRules.map(r => r.rule);
  }

  getAllRules(): SpamRule[] {
    return this.allRules.map(r => r.rule);
  }

  async refreshRules(): Promise<void> {
    this.lastLoadTime = null;
    await this.loadActiveRules();
  }

  clearCache(): void {
    this.allRules = [];
    this.numberPatternRules = [];
    this.behavioralRules = [];
    this.contentRules = [];
    this.lastLoadTime = null;
  }

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