/**
 * Algorithm Risk Scoring System - SEO Intelligence Integration Phase 5 Sprint 1
 *
 * @module planning/seo/lib/algorithm-risk-scoring
 * @description Evaluates SEO tactics against Google algorithm updates to warn against risky practices
 *              Provides risk scoring, mitigation strategies, and aggregate risk assessment
 */

import { promises as fs } from 'fs';
import * as path from 'path';
import * as yaml from 'js-yaml';

/**
 * Risk level classification
 */
export type RiskLevel = 'low' | 'medium' | 'high' | 'critical';

/**
 * Tactic risk evaluation result
 */
export interface TacticRiskEvaluation {
  /** Tactic identifier */
  tacticId: string;

  /** Tactic name */
  tacticName: string;

  /** Risk level classification */
  riskLevel: RiskLevel;

  /** Risk score (0.0-1.0) */
  riskScore: number;

  /** Algorithm updates that targeted this tactic */
  algorithmUpdates: string[];

  /** Mitigation strategies */
  mitigation: string[];

  /** Tactic description */
  description?: string;
}

/**
 * Aggregate risk score for multiple tactics
 */
export interface AggregateRiskScore {
  /** Overall risk level */
  overallRiskLevel: RiskLevel;

  /** Overall risk score (0.0-1.0) */
  overallRiskScore: number;

  /** Individual tactic evaluations */
  tacticEvaluations: TacticRiskEvaluation[];

  /** Critical tactics (should be avoided) */
  criticalTactics: TacticRiskEvaluation[];

  /** High-risk tactics (use with caution) */
  highRiskTactics: TacticRiskEvaluation[];

  /** Evaluation timestamp */
  evaluatedAt: string;
}

/**
 * Mitigation strategy for risky tactic
 */
export interface MitigationStrategy {
  /** Strategy identifier */
  id: string;

  /** Strategy description */
  description: string;

  /** Impact level (low/medium/high) */
  impact: 'low' | 'medium' | 'high';

  /** Implementation difficulty (easy/medium/hard) */
  difficulty: 'easy' | 'medium' | 'hard';

  /** Estimated effectiveness (0.0-1.0) */
  effectiveness: number;
}

/**
 * Tactic definition in risk database
 */
export interface TacticDefinition {
  /** Tactic identifier */
  id: string;

  /** Tactic name */
  name: string;

  /** Risk level */
  risk_level: RiskLevel;

  /** Risk score (0.0-1.0) */
  risk_score: number;

  /** Tactic description */
  description: string;

  /** Algorithm updates that targeted this */
  algorithm_updates: string[];

  /** Mitigation strategies */
  mitigation: string[];

  /** Additional metadata */
  metadata?: {
    category?: string;
    severity?: string;
    lastUpdated?: string;
  };
}

/**
 * Algorithm update definition
 */
export interface AlgorithmUpdate {
  /** Update identifier */
  id: string;

  /** Update name */
  name: string;

  /** Release date */
  date: string;

  /** Impact level (low/medium/high) */
  impact: 'low' | 'medium' | 'high';

  /** Tactics targeted by this update */
  targeted_tactics: string[];

  /** Update description */
  description: string;

  /** Additional metadata */
  metadata?: {
    source?: string;
    rolloutDuration?: string;
  };
}

/**
 * Risk database structure
 */
export interface RiskDatabase {
  /** Tactic definitions */
  tactics: TacticDefinition[];

  /** Algorithm update history */
  algorithmUpdates: AlgorithmUpdate[];

  /** Database metadata */
  metadata?: {
    version?: string;
    lastUpdated?: string;
  };
}

/**
 * Risk scoring error
 */
export class RiskScoringError extends Error {
  constructor(
    message: string,
    public code: 'DATABASE_LOAD_FAILED' | 'TACTIC_NOT_FOUND' | 'INVALID_RISK_SCORE' | 'VALIDATION_FAILED',
    public details?: unknown
  ) {
    super(message);
    this.name = 'RiskScoringError';
  }
}

// Global database cache
let cachedDatabase: RiskDatabase | null = null;

/**
 * Load risk database from YAML files
 *
 * @param baseDir - Base directory for risk database (default: ~/.cfn/seo/global-knowledge/algorithm-intelligence)
 * @returns Loaded risk database
 */
export async function loadRiskDatabase(
  baseDir: string = path.join(process.env.HOME || '/home/masharratt', '.cfn/seo/global-knowledge/algorithm-intelligence')
): Promise<RiskDatabase> {
  try {
    // Return cached database if available
    if (cachedDatabase) {
      return cachedDatabase;
    }

    const riskScoresPath = path.join(baseDir, 'risk-scores.yaml');
    const updateHistoryPath = path.join(baseDir, 'update-history.yaml');

    // Load YAML files
    const riskScoresContent = await fs.readFile(riskScoresPath, 'utf-8');
    const updateHistoryContent = await fs.readFile(updateHistoryPath, 'utf-8');

    // Parse YAML
    const riskScoresData = yaml.load(riskScoresContent) as { tactics: TacticDefinition[] };
    const updateHistoryData = yaml.load(updateHistoryContent) as { algorithm_updates: AlgorithmUpdate[] };

    // Validate structure
    if (!riskScoresData.tactics || !Array.isArray(riskScoresData.tactics)) {
      throw new RiskScoringError('Invalid risk scores structure: missing tactics array', 'DATABASE_LOAD_FAILED');
    }

    if (!updateHistoryData.algorithm_updates || !Array.isArray(updateHistoryData.algorithm_updates)) {
      throw new RiskScoringError('Invalid update history structure: missing algorithm_updates array', 'DATABASE_LOAD_FAILED');
    }

    // Validate minimum counts
    if (riskScoresData.tactics.length < 20) {
      throw new RiskScoringError(
        `Risk database must contain at least 20 tactics, found ${riskScoresData.tactics.length}`,
        'VALIDATION_FAILED'
      );
    }

    if (updateHistoryData.algorithm_updates.length < 10) {
      throw new RiskScoringError(
        `Update history must contain at least 10 updates, found ${updateHistoryData.algorithm_updates.length}`,
        'VALIDATION_FAILED'
      );
    }

    // Validate risk scores are in valid range (0.0-1.0)
    for (const tactic of riskScoresData.tactics) {
      if (tactic.risk_score < 0 || tactic.risk_score > 1) {
        throw new RiskScoringError(
          `Invalid risk score for tactic ${tactic.id}: ${tactic.risk_score} (must be 0.0-1.0)`,
          'INVALID_RISK_SCORE'
        );
      }
    }

    // Build database
    const database: RiskDatabase = {
      tactics: riskScoresData.tactics,
      algorithmUpdates: updateHistoryData.algorithm_updates,
      metadata: {
        version: '1.0.0',
        lastUpdated: new Date().toISOString(),
      },
    };

    // Cache database
    cachedDatabase = database;

    return database;
  } catch (error) {
    if (error instanceof RiskScoringError) {
      throw error;
    }
    throw new RiskScoringError('Failed to load risk database', 'DATABASE_LOAD_FAILED', error);
  }
}

/**
 * Evaluate single tactic against risk database
 *
 * @param tacticId - Tactic identifier
 * @param database - Risk database (optional, will load if not provided)
 * @returns Tactic risk evaluation
 */
export async function evaluateTactic(
  tacticId: string,
  database?: RiskDatabase
): Promise<TacticRiskEvaluation> {
  try {
    // Input validation: prevent injection attacks
    const VALID_TACTIC_ID_REGEX = /^[a-zA-Z0-9_-]+$/;
    if (!VALID_TACTIC_ID_REGEX.test(tacticId)) {
      throw new RiskScoringError(`Invalid tactic ID format: ${tacticId}`, 'TACTIC_NOT_FOUND');
    }

    // Load database if not provided
    const db = database || (await loadRiskDatabase());

    // Find tactic in database
    const tactic = db.tactics.find((t) => t.id === tacticId);

    if (!tactic) {
      throw new RiskScoringError(`Tactic not found: ${tacticId}`, 'TACTIC_NOT_FOUND');
    }

    // Build evaluation
    const evaluation: TacticRiskEvaluation = {
      tacticId: tactic.id,
      tacticName: tactic.name,
      riskLevel: tactic.risk_level,
      riskScore: tactic.risk_score,
      algorithmUpdates: tactic.algorithm_updates,
      mitigation: tactic.mitigation,
      description: tactic.description,
    };

    return evaluation;
  } catch (error) {
    if (error instanceof RiskScoringError) {
      throw error;
    }
    throw new RiskScoringError(`Failed to evaluate tactic ${tacticId}`, 'VALIDATION_FAILED', error);
  }
}

/**
 * Calculate aggregate risk score for multiple tactics
 *
 * @param tacticIds - Array of tactic identifiers
 * @param database - Risk database (optional, will load if not provided)
 * @returns Aggregate risk score
 */
export async function calculateAggregateRisk(
  tacticIds: string[],
  database?: RiskDatabase
): Promise<AggregateRiskScore> {
  try {
    // Load database if not provided
    const db = database || (await loadRiskDatabase());

    // Evaluate each tactic
    const tacticEvaluations: TacticRiskEvaluation[] = [];

    for (const tacticId of tacticIds) {
      try {
        const evaluation = await evaluateTactic(tacticId, db);
        tacticEvaluations.push(evaluation);
      } catch (error) {
        // Skip invalid tactic IDs
        console.warn(`Skipping invalid tactic: ${tacticId}`);
      }
    }

    // Calculate overall risk score (weighted average)
    const overallRiskScore =
      tacticEvaluations.length > 0
        ? tacticEvaluations.reduce((sum, t) => sum + t.riskScore, 0) / tacticEvaluations.length
        : 0;

    // Determine overall risk level
    let overallRiskLevel: RiskLevel;
    if (overallRiskScore >= 0.8) {
      overallRiskLevel = 'critical';
    } else if (overallRiskScore >= 0.6) {
      overallRiskLevel = 'high';
    } else if (overallRiskScore >= 0.4) {
      overallRiskLevel = 'medium';
    } else {
      overallRiskLevel = 'low';
    }

    // Separate critical and high-risk tactics
    const criticalTactics = tacticEvaluations.filter((t) => t.riskLevel === 'critical');
    const highRiskTactics = tacticEvaluations.filter((t) => t.riskLevel === 'high');

    return {
      overallRiskLevel,
      overallRiskScore,
      tacticEvaluations,
      criticalTactics,
      highRiskTactics,
      evaluatedAt: new Date().toISOString(),
    };
  } catch (error) {
    throw new RiskScoringError('Failed to calculate aggregate risk', 'VALIDATION_FAILED', error);
  }
}

/**
 * Get mitigation strategies for a tactic
 *
 * @param tacticId - Tactic identifier
 * @param database - Risk database (optional, will load if not provided)
 * @returns Array of mitigation strategies
 */
export async function getMitigationStrategies(
  tacticId: string,
  database?: RiskDatabase
): Promise<MitigationStrategy[]> {
  try {
    // Evaluate tactic
    const evaluation = await evaluateTactic(tacticId, database);

    // Build mitigation strategies from tactic mitigation list
    const strategies: MitigationStrategy[] = evaluation.mitigation.map((mitigation, index) => {
      // Estimate impact and difficulty based on risk level
      let impact: 'low' | 'medium' | 'high' = 'medium';
      let difficulty: 'easy' | 'medium' | 'hard' = 'medium';
      let effectiveness = 0.7;

      if (evaluation.riskLevel === 'critical') {
        impact = 'high';
        difficulty = 'hard';
        effectiveness = 0.9;
      } else if (evaluation.riskLevel === 'high') {
        impact = 'high';
        difficulty = 'medium';
        effectiveness = 0.8;
      } else if (evaluation.riskLevel === 'medium') {
        impact = 'medium';
        difficulty = 'medium';
        effectiveness = 0.7;
      } else {
        impact = 'low';
        difficulty = 'easy';
        effectiveness = 0.6;
      }

      return {
        id: `${tacticId}-mitigation-${index + 1}`,
        description: mitigation,
        impact,
        difficulty,
        effectiveness,
      };
    });

    return strategies;
  } catch (error) {
    throw new RiskScoringError(`Failed to get mitigation strategies for ${tacticId}`, 'VALIDATION_FAILED', error);
  }
}

/**
 * Get algorithm updates that targeted a specific tactic
 *
 * @param tacticId - Tactic identifier
 * @param database - Risk database (optional, will load if not provided)
 * @returns Array of algorithm updates
 */
export async function getAlgorithmUpdatesForTactic(
  tacticId: string,
  database?: RiskDatabase
): Promise<AlgorithmUpdate[]> {
  try {
    // Load database if not provided
    const db = database || (await loadRiskDatabase());

    // Evaluate tactic to get update IDs
    const evaluation = await evaluateTactic(tacticId, db);

    // Find full update details
    const updates = db.algorithmUpdates.filter((update) =>
      evaluation.algorithmUpdates.includes(update.id)
    );

    return updates;
  } catch (error) {
    throw new RiskScoringError(`Failed to get algorithm updates for ${tacticId}`, 'VALIDATION_FAILED', error);
  }
}

/**
 * Clear cached database (for testing)
 */
export function clearDatabaseCache(): void {
  cachedDatabase = null;
}
