/**
 * QualityAssuranceAgent
 * Verifies blockchain integrity and data quality
 *
 * Responsibilities:
 * - Verify blockchain integrity
 * - Detect data anomalies and inconsistencies
 * - Monitor transaction validity
 * - Generate data quality reports
 * - Ensure compliance with data standards
 */

import { BaseAgent } from './BaseAgent';
import { AgentConfig, AgentTask, QualityReport } from './types';
import { getBlockchain } from '../blockchain/manager';
import { getTransportChain } from '../transport/tracker';
import { PlantBlock } from '../blockchain/PlantBlock';
import * as crypto from 'crypto';

interface IntegrityCheck {
  chainId: string;
  chainName: string;
  isValid: boolean;
  blocksChecked: number;
  hashMismatches: number;
  linkBroken: number;
  timestamp: string;
}

interface DataQualityIssue {
  id: string;
  chainId: string;
  blockIndex: number;
  issueType: 'missing_data' | 'invalid_format' | 'out_of_range' | 'duplicate' | 'inconsistent' | 'suspicious';
  field: string;
  description: string;
  severity: 'low' | 'medium' | 'high' | 'critical';
  autoFixable: boolean;
  suggestedFix?: string;
}

interface ComplianceStatus {
  standard: string;
  compliant: boolean;
  violations: string[];
  score: number;
}

interface DataStatistics {
  totalRecords: number;
  completeRecords: number;
  partialRecords: number;
  invalidRecords: number;
  completenessScore: number;
  accuracyScore: number;
  consistencyScore: number;
  timelinessScore: number;
}

interface AuditLog {
  id: string;
  timestamp: string;
  action: 'verify' | 'fix' | 'flag' | 'report';
  target: string;
  result: 'pass' | 'fail' | 'warning';
  details: string;
}

export class QualityAssuranceAgent extends BaseAgent {
  private integrityChecks: IntegrityCheck[] = [];
  private qualityIssues: DataQualityIssue[] = [];
  private complianceStatus: ComplianceStatus[] = [];
  private auditLog: AuditLog[] = [];
  private dataStats: DataStatistics | null = null;

  constructor() {
    const config: AgentConfig = {
      id: 'quality-assurance-agent',
      name: 'Quality Assurance Agent',
      description: 'Verifies data integrity and quality across all chains',
      enabled: true,
      intervalMs: 120000, // Run every 2 minutes
      priority: 'critical',
      maxRetries: 5,
      timeoutMs: 60000
    };
    super(config);
  }

  /**
   * Main execution cycle
   */
  async runOnce(): Promise<AgentTask | null> {
    const startTime = Date.now();

    // Verify blockchain integrity
    const plantChainCheck = await this.verifyPlantChain();
    const transportChainCheck = await this.verifyTransportChain();

    this.integrityChecks = [plantChainCheck, transportChainCheck];

    // Check data quality
    const issues = this.checkDataQuality();
    this.qualityIssues = [...this.qualityIssues, ...issues].slice(-500);

    // Check compliance
    this.complianceStatus = this.checkCompliance();

    // Calculate statistics
    this.dataStats = this.calculateStatistics();

    // Log audit
    this.addAuditLog('verify', 'all_chains',
      plantChainCheck.isValid && transportChainCheck.isValid ? 'pass' : 'fail',
      `Plant chain: ${plantChainCheck.isValid}, Transport chain: ${transportChainCheck.isValid}`
    );

    // Generate alerts for critical issues
    this.generateQualityAlerts();

    return this.createTaskResult('quality_assurance', 'completed', {
      integrityValid: plantChainCheck.isValid && transportChainCheck.isValid,
      issuesFound: issues.length,
      complianceScore: this.calculateOverallCompliance(),
      executionTimeMs: Date.now() - startTime
    });
  }

  /**
   * Verify plant blockchain integrity
   */
  private async verifyPlantChain(): Promise<IntegrityCheck> {
    const blockchain = getBlockchain();
    const chain = blockchain.chain;

    let hashMismatches = 0;
    let linkBroken = 0;

    for (let i = 1; i < chain.length; i++) {
      const block = chain[i];
      const prevBlock = chain[i - 1];

      // Verify hash
      const expectedHash = this.calculateBlockHash(block);
      if (block.hash !== expectedHash) {
        hashMismatches++;
      }

      // Verify chain link
      if (block.previousHash !== prevBlock.hash) {
        linkBroken++;
      }
    }

    const isValid = hashMismatches === 0 && linkBroken === 0;

    if (!isValid) {
      this.createAlert('critical', 'Plant Chain Integrity Compromised',
        `Found ${hashMismatches} hash mismatches and ${linkBroken} broken links`,
        { actionRequired: 'Immediate investigation required' }
      );
    }

    return {
      chainId: 'plant-chain',
      chainName: 'Plant Lineage Chain',
      isValid,
      blocksChecked: chain.length,
      hashMismatches,
      linkBroken,
      timestamp: new Date().toISOString()
    };
  }

  /**
   * Verify transport blockchain integrity
   */
  private async verifyTransportChain(): Promise<IntegrityCheck> {
    const transportChain = getTransportChain();
    const isValid = transportChain.isChainValid();

    return {
      chainId: 'transport-chain',
      chainName: 'Transport Events Chain',
      isValid,
      blocksChecked: transportChain.chain.length,
      hashMismatches: isValid ? 0 : 1,
      linkBroken: isValid ? 0 : 1,
      timestamp: new Date().toISOString()
    };
  }

  /**
   * Calculate block hash (must match PlantChain implementation)
   */
  private calculateBlockHash(block: PlantBlock): string {
    const data = `${block.index}${block.timestamp}${JSON.stringify(block.plant)}${block.previousHash}${block.nonce}`;
    return crypto.createHash('sha256').update(data).digest('hex');
  }

  /**
   * Check data quality across chains
   */
  private checkDataQuality(): DataQualityIssue[] {
    const issues: DataQualityIssue[] = [];

    const blockchain = getBlockchain();
    const chain = blockchain.chain.slice(1);

    const seenIds = new Set<string>();

    for (let i = 0; i < chain.length; i++) {
      const block = chain[i];
      const plant = block.plant;

      // Check for duplicates
      if (seenIds.has(plant.id)) {
        issues.push({
          id: `issue-${Date.now()}-${i}`,
          chainId: 'plant-chain',
          blockIndex: block.index,
          issueType: 'duplicate',
          field: 'plant.id',
          description: `Duplicate plant ID: ${plant.id}`,
          severity: 'high',
          autoFixable: false
        });
      }
      seenIds.add(plant.id);

      // Check for missing required fields
      if (!plant.commonName) {
        issues.push({
          id: `issue-${Date.now()}-${i}-name`,
          chainId: 'plant-chain',
          blockIndex: block.index,
          issueType: 'missing_data',
          field: 'plant.commonName',
          description: 'Missing common name',
          severity: 'medium',
          autoFixable: false
        });
      }

      // Check location validity
      if (plant.location) {
        if (Math.abs(plant.location.latitude) > 90) {
          issues.push({
            id: `issue-${Date.now()}-${i}-lat`,
            chainId: 'plant-chain',
            blockIndex: block.index,
            issueType: 'out_of_range',
            field: 'plant.location.latitude',
            description: `Invalid latitude: ${plant.location.latitude}`,
            severity: 'high',
            autoFixable: false
          });
        }

        if (Math.abs(plant.location.longitude) > 180) {
          issues.push({
            id: `issue-${Date.now()}-${i}-lon`,
            chainId: 'plant-chain',
            blockIndex: block.index,
            issueType: 'out_of_range',
            field: 'plant.location.longitude',
            description: `Invalid longitude: ${plant.location.longitude}`,
            severity: 'high',
            autoFixable: false
          });
        }
      }

      // Check generation consistency
      if (plant.parentPlantId) {
        const parent = chain.find(b => b.plant.id === plant.parentPlantId);
        if (parent && plant.generation !== parent.plant.generation + 1) {
          issues.push({
            id: `issue-${Date.now()}-${i}-gen`,
            chainId: 'plant-chain',
            blockIndex: block.index,
            issueType: 'inconsistent',
            field: 'plant.generation',
            description: `Generation ${plant.generation} inconsistent with parent generation ${parent.plant.generation}`,
            severity: 'medium',
            autoFixable: true,
            suggestedFix: `Set generation to ${parent.plant.generation + 1}`
          });
        }
      }

      // Check for suspicious patterns
      if (plant.childPlants && plant.childPlants.length > 100) {
        issues.push({
          id: `issue-${Date.now()}-${i}-children`,
          chainId: 'plant-chain',
          blockIndex: block.index,
          issueType: 'suspicious',
          field: 'plant.childPlants',
          description: `Unusually high number of children: ${plant.childPlants.length}`,
          severity: 'low',
          autoFixable: false
        });
      }

      // Check timestamp validity
      const blockDate = new Date(block.timestamp);
      if (blockDate > new Date()) {
        issues.push({
          id: `issue-${Date.now()}-${i}-time`,
          chainId: 'plant-chain',
          blockIndex: block.index,
          issueType: 'invalid_format',
          field: 'timestamp',
          description: 'Timestamp is in the future',
          severity: 'high',
          autoFixable: false
        });
      }
    }

    return issues;
  }

  /**
   * Check compliance with data standards
   */
  private checkCompliance(): ComplianceStatus[] {
    const statuses: ComplianceStatus[] = [];

    // Blockchain Integrity Standard
    const integrityViolations: string[] = [];
    for (const check of this.integrityChecks) {
      if (!check.isValid) {
        integrityViolations.push(`${check.chainName} failed integrity check`);
      }
    }
    statuses.push({
      standard: 'Blockchain Integrity',
      compliant: integrityViolations.length === 0,
      violations: integrityViolations,
      score: integrityViolations.length === 0 ? 100 : 0
    });

    // Data Completeness Standard (>90% complete records)
    const completeness = this.dataStats?.completenessScore || 0;
    statuses.push({
      standard: 'Data Completeness (>90%)',
      compliant: completeness >= 90,
      violations: completeness < 90 ? [`Completeness at ${completeness}%`] : [],
      score: completeness
    });

    // Location Accuracy Standard
    const locationIssues = this.qualityIssues.filter(i =>
      i.field.includes('location') && i.severity === 'high'
    );
    statuses.push({
      standard: 'Location Data Accuracy',
      compliant: locationIssues.length === 0,
      violations: locationIssues.map(i => i.description),
      score: Math.max(0, 100 - locationIssues.length * 10)
    });

    // No Duplicates Standard
    const duplicateIssues = this.qualityIssues.filter(i => i.issueType === 'duplicate');
    statuses.push({
      standard: 'No Duplicate Records',
      compliant: duplicateIssues.length === 0,
      violations: duplicateIssues.map(i => i.description),
      score: duplicateIssues.length === 0 ? 100 : 0
    });

    // Lineage Consistency Standard
    const lineageIssues = this.qualityIssues.filter(i =>
      i.issueType === 'inconsistent' && i.field.includes('generation')
    );
    statuses.push({
      standard: 'Lineage Consistency',
      compliant: lineageIssues.length === 0,
      violations: lineageIssues.map(i => i.description),
      score: Math.max(0, 100 - lineageIssues.length * 5)
    });

    return statuses;
  }

  /**
   * Calculate data statistics
   */
  private calculateStatistics(): DataStatistics {
    const blockchain = getBlockchain();
    const chain = blockchain.chain.slice(1);

    let completeRecords = 0;
    let partialRecords = 0;
    let invalidRecords = 0;

    for (const block of chain) {
      const plant = block.plant;

      // Check completeness
      const hasRequiredFields = plant.id && plant.commonName && plant.location && plant.owner;
      const hasOptionalFields = plant.environment && plant.growthMetrics;

      if (!hasRequiredFields) {
        invalidRecords++;
      } else if (hasOptionalFields) {
        completeRecords++;
      } else {
        partialRecords++;
      }
    }

    const totalRecords = chain.length;
    const completenessScore = totalRecords > 0
      ? Math.round(((completeRecords + partialRecords * 0.5) / totalRecords) * 100)
      : 0;

    // Calculate accuracy (based on issues found)
    const highSeverityIssues = this.qualityIssues.filter(i =>
      i.severity === 'high' || i.severity === 'critical'
    ).length;
    const accuracyScore = Math.max(0, 100 - highSeverityIssues * 5);

    // Consistency score (based on inconsistency issues)
    const inconsistencyIssues = this.qualityIssues.filter(i =>
      i.issueType === 'inconsistent'
    ).length;
    const consistencyScore = Math.max(0, 100 - inconsistencyIssues * 3);

    // Timeliness (based on recent data)
    const oneWeekAgo = Date.now() - 7 * 24 * 60 * 60 * 1000;
    const recentRecords = chain.filter(b =>
      new Date(b.timestamp).getTime() > oneWeekAgo
    ).length;
    const timelinessScore = totalRecords > 0
      ? Math.min(100, Math.round((recentRecords / Math.max(1, totalRecords * 0.1)) * 100))
      : 0;

    return {
      totalRecords,
      completeRecords,
      partialRecords,
      invalidRecords,
      completenessScore,
      accuracyScore,
      consistencyScore,
      timelinessScore
    };
  }

  /**
   * Add audit log entry
   */
  private addAuditLog(
    action: AuditLog['action'],
    target: string,
    result: AuditLog['result'],
    details: string
  ): void {
    this.auditLog.push({
      id: `audit-${Date.now()}-${Math.random().toString(36).substr(2, 5)}`,
      timestamp: new Date().toISOString(),
      action,
      target,
      result,
      details
    });

    // Keep last 1000 entries
    if (this.auditLog.length > 1000) {
      this.auditLog = this.auditLog.slice(-1000);
    }
  }

  /**
   * Generate alerts for quality issues
   */
  private generateQualityAlerts(): void {
    const criticalIssues = this.qualityIssues.filter(i =>
      i.severity === 'critical'
    );

    for (const issue of criticalIssues.slice(0, 5)) {
      this.createAlert('critical', `Data Quality Issue: ${issue.issueType}`,
        issue.description,
        {
          actionRequired: issue.suggestedFix || 'Manual investigation required',
          relatedEntityId: `block-${issue.blockIndex}`,
          relatedEntityType: 'block'
        }
      );
    }

    // Alert for low compliance
    for (const status of this.complianceStatus) {
      if (!status.compliant && status.score < 50) {
        this.createAlert('warning', `Low Compliance: ${status.standard}`,
          `Compliance score: ${status.score}%. Violations: ${status.violations.length}`,
          { actionRequired: 'Review and address compliance violations' }
        );
      }
    }
  }

  /**
   * Calculate overall compliance score
   */
  private calculateOverallCompliance(): number {
    if (this.complianceStatus.length === 0) return 0;
    return Math.round(
      this.complianceStatus.reduce((sum, s) => sum + s.score, 0) / this.complianceStatus.length
    );
  }

  /**
   * Generate quality report
   */
  generateReport(): QualityReport {
    const chainCheck = this.integrityChecks.find(c => c.chainId === 'plant-chain');

    return {
      chainId: 'plant-chain',
      isValid: chainCheck?.isValid || false,
      blocksVerified: chainCheck?.blocksChecked || 0,
      integrityScore: this.calculateOverallCompliance(),
      issues: this.qualityIssues.slice(0, 10).map(i => ({
        blockIndex: i.blockIndex,
        issueType: i.issueType,
        description: i.description,
        severity: i.severity
      })),
      lastVerifiedAt: new Date().toISOString()
    };
  }

  /**
   * Get integrity checks
   */
  getIntegrityChecks(): IntegrityCheck[] {
    return this.integrityChecks;
  }

  /**
   * Get quality issues
   */
  getQualityIssues(severity?: string): DataQualityIssue[] {
    if (severity) {
      return this.qualityIssues.filter(i => i.severity === severity);
    }
    return this.qualityIssues;
  }

  /**
   * Get compliance status
   */
  getComplianceStatus(): ComplianceStatus[] {
    return this.complianceStatus;
  }

  /**
   * Get data statistics
   */
  getDataStatistics(): DataStatistics | null {
    return this.dataStats;
  }

  /**
   * Get audit log
   */
  getAuditLog(limit: number = 100): AuditLog[] {
    return this.auditLog.slice(-limit);
  }

  /**
   * Manually trigger verification
   */
  async triggerVerification(): Promise<{
    plantChain: IntegrityCheck;
    transportChain: IntegrityCheck;
    overallValid: boolean;
  }> {
    const plantChain = await this.verifyPlantChain();
    const transportChain = await this.verifyTransportChain();

    this.addAuditLog('verify', 'manual_trigger',
      plantChain.isValid && transportChain.isValid ? 'pass' : 'fail',
      'Manual verification triggered'
    );

    return {
      plantChain,
      transportChain,
      overallValid: plantChain.isValid && transportChain.isValid
    };
  }
}

// Singleton instance
let qaAgentInstance: QualityAssuranceAgent | null = null;

export function getQualityAssuranceAgent(): QualityAssuranceAgent {
  if (!qaAgentInstance) {
    qaAgentInstance = new QualityAssuranceAgent();
  }
  return qaAgentInstance;
}
