/**
 * NetworkDiscoveryAgent
 * Analyzes geographic distribution and connections in the plant network
 *
 * Responsibilities:
 * - Map plant distribution across regions
 * - Identify network hotspots and clusters
 * - Suggest grower/consumer connections
 * - Track network growth patterns
 * - Detect coverage gaps
 */

import { BaseAgent } from './BaseAgent';
import { AgentConfig, AgentTask, NetworkAnalysis } from './types';
import { getBlockchain } from '../blockchain/manager';
import { PlantBlock } from '../blockchain/types';

interface NetworkNode {
  id: string;
  type: 'grower' | 'consumer' | 'plant';
  location: { latitude: number; longitude: number };
  connections: string[];
  activityScore: number;
  species?: string[];
  lastActive: string;
}

interface NetworkCluster {
  id: string;
  centroid: { latitude: number; longitude: number };
  nodes: NetworkNode[];
  radius: number;
  density: number;
  dominantSpecies: string[];
  activityLevel: 'low' | 'medium' | 'high';
}

interface CoverageGap {
  id: string;
  location: { latitude: number; longitude: number };
  nearestCluster: string;
  distanceToNearest: number;
  populationDensity: 'urban' | 'suburban' | 'rural';
  potentialDemand: number;
  recommendation: string;
}

interface ConnectionSuggestion {
  id: string;
  node1Id: string;
  node2Id: string;
  distance: number;
  reason: string;
  strength: number; // 0-100
  mutualBenefits: string[];
}

interface NetworkGrowth {
  date: string;
  totalNodes: number;
  totalConnections: number;
  newNodesWeek: number;
  newConnectionsWeek: number;
  geographicExpansion: number; // km radius growth
}

interface RegionalStats {
  region: string;
  centerLat: number;
  centerLon: number;
  nodeCount: number;
  plantCount: number;
  uniqueSpecies: number;
  avgActivityScore: number;
  connections: number;
}

export class NetworkDiscoveryAgent extends BaseAgent {
  private nodes: Map<string, NetworkNode> = new Map();
  private clusters: NetworkCluster[] = [];
  private coverageGaps: CoverageGap[] = [];
  private connectionSuggestions: ConnectionSuggestion[] = [];
  private growthHistory: NetworkGrowth[] = [];
  private regionalStats: Map<string, RegionalStats> = new Map();

  constructor() {
    const config: AgentConfig = {
      id: 'network-discovery-agent',
      name: 'Network Discovery Agent',
      description: 'Maps and analyzes the geographic plant network',
      enabled: true,
      intervalMs: 600000, // Run every 10 minutes
      priority: 'medium',
      maxRetries: 3,
      timeoutMs: 60000
    };
    super(config);
  }

  /**
   * Main execution cycle
   */
  async runOnce(): Promise<AgentTask | null> {
    const blockchain = getBlockchain();
    const chain = blockchain.getChain();
    const plants = chain.slice(1);

    // Build network from plant data
    this.buildNetworkNodes(plants);

    // Identify clusters
    this.identifyClusters();

    // Find coverage gaps
    this.findCoverageGaps();

    // Generate connection suggestions
    this.generateConnectionSuggestions();

    // Update regional statistics
    this.updateRegionalStats();

    // Track growth
    this.trackGrowth();

    // Generate alerts for significant network events
    this.checkNetworkAlerts();

    return this.createTaskResult('network_discovery', 'completed', {
      totalNodes: this.nodes.size,
      clustersIdentified: this.clusters.length,
      coverageGaps: this.coverageGaps.length,
      connectionSuggestions: this.connectionSuggestions.length,
      regions: this.regionalStats.size
    });
  }

  /**
   * Build network nodes from plant data
   */
  private buildNetworkNodes(plants: PlantBlock[]): void {
    // Group by owner to create nodes
    const ownerPlants = new Map<string, PlantBlock[]>();

    for (const block of plants) {
      const ownerId = block.plant.owner?.id || 'unknown';
      const ownerGroup = ownerPlants.get(ownerId) || [];
      ownerGroup.push(block);
      ownerPlants.set(ownerId, ownerGroup);
    }

    // Create nodes for each owner
    for (const [ownerId, ownerBlocks] of ownerPlants) {
      const latestBlock = ownerBlocks[ownerBlocks.length - 1];
      const species = [...new Set(ownerBlocks.map(b => b.plant.commonName).filter(Boolean))];

      // Calculate connections (plants from same lineage)
      const connections: string[] = [];
      for (const block of ownerBlocks) {
        if (block.plant.parentPlantId) {
          const parentOwner = plants.find(p => p.plant.id === block.plant.parentPlantId)?.plant.owner?.id;
          if (parentOwner && parentOwner !== ownerId && !connections.includes(parentOwner)) {
            connections.push(parentOwner);
          }
        }
        for (const childId of block.plant.childPlants || []) {
          const childOwner = plants.find(p => p.plant.id === childId)?.plant.owner?.id;
          if (childOwner && childOwner !== ownerId && !connections.includes(childOwner)) {
            connections.push(childOwner);
          }
        }
      }

      // Calculate activity score
      const recentActivity = ownerBlocks.filter(b => {
        const age = Date.now() - new Date(b.timestamp).getTime();
        return age < 30 * 24 * 60 * 60 * 1000; // Last 30 days
      }).length;

      const node: NetworkNode = {
        id: ownerId,
        type: ownerBlocks.length > 5 ? 'grower' : 'consumer',
        location: latestBlock.plant.location,
        connections,
        activityScore: Math.min(100, 20 + recentActivity * 10 + connections.length * 5),
        species: species as string[],
        lastActive: latestBlock.timestamp
      };

      this.nodes.set(ownerId, node);
    }

    // Also create plant nodes for visualization
    for (const block of plants) {
      const plantNode: NetworkNode = {
        id: `plant-${block.plant.id}`,
        type: 'plant',
        location: block.plant.location,
        connections: block.plant.childPlants?.map(c => `plant-${c}`) || [],
        activityScore: block.plant.status === 'growing' ? 80 : 40,
        species: [block.plant.commonName].filter(Boolean) as string[],
        lastActive: block.timestamp
      };

      this.nodes.set(plantNode.id, plantNode);
    }
  }

  /**
   * Identify clusters using simplified DBSCAN-like algorithm
   */
  private identifyClusters(): void {
    const nodes = Array.from(this.nodes.values()).filter(n => n.type !== 'plant');
    const clusterRadius = 50; // km
    const minClusterSize = 2;

    const visited = new Set<string>();
    const clusters: NetworkCluster[] = [];

    for (const node of nodes) {
      if (visited.has(node.id)) continue;

      // Find all nodes within radius
      const neighborhood = nodes.filter(n =>
        !visited.has(n.id) &&
        this.calculateDistance(node.location, n.location) <= clusterRadius
      );

      if (neighborhood.length >= minClusterSize) {
        // Create cluster
        const clusterNodes: NetworkNode[] = [];

        for (const neighbor of neighborhood) {
          visited.add(neighbor.id);
          clusterNodes.push(neighbor);
        }

        // Calculate centroid
        const centroid = {
          latitude: clusterNodes.reduce((sum, n) => sum + n.location.latitude, 0) / clusterNodes.length,
          longitude: clusterNodes.reduce((sum, n) => sum + n.location.longitude, 0) / clusterNodes.length
        };

        // Calculate radius
        const maxDist = Math.max(...clusterNodes.map(n =>
          this.calculateDistance(centroid, n.location)
        ));

        // Find dominant species
        const speciesCounts = new Map<string, number>();
        for (const n of clusterNodes) {
          for (const species of n.species || []) {
            speciesCounts.set(species, (speciesCounts.get(species) || 0) + 1);
          }
        }
        const dominantSpecies = Array.from(speciesCounts.entries())
          .sort((a, b) => b[1] - a[1])
          .slice(0, 3)
          .map(([species]) => species);

        // Calculate activity level
        const avgActivity = clusterNodes.reduce((sum, n) => sum + n.activityScore, 0) / clusterNodes.length;
        const activityLevel = avgActivity > 70 ? 'high' : avgActivity > 40 ? 'medium' : 'low';

        clusters.push({
          id: `cluster-${Date.now()}-${Math.random().toString(36).substr(2, 5)}`,
          centroid,
          nodes: clusterNodes,
          radius: Math.round(maxDist * 10) / 10,
          density: clusterNodes.length / (Math.PI * maxDist * maxDist),
          dominantSpecies,
          activityLevel
        });
      }
    }

    this.clusters = clusters;
  }

  /**
   * Find coverage gaps in the network
   */
  private findCoverageGaps(): void {
    // Define key metropolitan areas that should have coverage
    const keyAreas = [
      { name: 'Metro Center', lat: 40.7128, lon: -74.0060, pop: 'urban' },
      { name: 'Suburban North', lat: 40.85, lon: -73.95, pop: 'suburban' },
      { name: 'Suburban South', lat: 40.55, lon: -74.15, pop: 'suburban' },
      { name: 'Rural West', lat: 40.7, lon: -74.4, pop: 'rural' }
    ];

    const gaps: CoverageGap[] = [];

    for (const area of keyAreas) {
      // Find nearest cluster
      let nearestCluster: NetworkCluster | null = null;
      let nearestDistance = Infinity;

      for (const cluster of this.clusters) {
        const dist = this.calculateDistance(
          { latitude: area.lat, longitude: area.lon },
          cluster.centroid
        );
        if (dist < nearestDistance) {
          nearestDistance = dist;
          nearestCluster = cluster;
        }
      }

      // If no cluster within 30km, it's a gap
      if (nearestDistance > 30) {
        const potentialDemand = area.pop === 'urban' ? 1000 :
          area.pop === 'suburban' ? 500 : 100;

        gaps.push({
          id: `gap-${Date.now()}-${Math.random().toString(36).substr(2, 5)}`,
          location: { latitude: area.lat, longitude: area.lon },
          nearestCluster: nearestCluster?.id || 'none',
          distanceToNearest: Math.round(nearestDistance),
          populationDensity: area.pop as 'urban' | 'suburban' | 'rural',
          potentialDemand,
          recommendation: `Recruit growers in ${area.name} area to serve ${potentialDemand}+ potential consumers`
        });
      }
    }

    this.coverageGaps = gaps;
  }

  /**
   * Generate connection suggestions
   */
  private generateConnectionSuggestions(): void {
    const nodes = Array.from(this.nodes.values()).filter(n => n.type !== 'plant');
    const suggestions: ConnectionSuggestion[] = [];

    for (const node1 of nodes) {
      for (const node2 of nodes) {
        if (node1.id >= node2.id) continue; // Avoid duplicates
        if (node1.connections.includes(node2.id)) continue; // Already connected

        const distance = this.calculateDistance(node1.location, node2.location);
        if (distance > 100) continue; // Too far

        // Calculate connection strength
        let strength = 50;
        const benefits: string[] = [];

        // Distance bonus
        if (distance < 10) {
          strength += 20;
          benefits.push('Very close proximity');
        } else if (distance < 25) {
          strength += 10;
          benefits.push('Local connection');
        }

        // Complementary types
        if (node1.type !== node2.type) {
          strength += 15;
          benefits.push('Grower-consumer match');
        }

        // Shared species interest
        const sharedSpecies = node1.species?.filter(s => node2.species?.includes(s)) || [];
        if (sharedSpecies.length > 0) {
          strength += sharedSpecies.length * 5;
          benefits.push(`Shared interest: ${sharedSpecies.join(', ')}`);
        }

        // Activity match
        if (Math.abs(node1.activityScore - node2.activityScore) < 20) {
          strength += 10;
          benefits.push('Similar activity levels');
        }

        if (strength >= 60) {
          suggestions.push({
            id: `suggestion-${Date.now()}-${Math.random().toString(36).substr(2, 5)}`,
            node1Id: node1.id,
            node2Id: node2.id,
            distance: Math.round(distance * 10) / 10,
            reason: `${benefits[0] || 'Proximity match'}`,
            strength: Math.min(100, strength),
            mutualBenefits: benefits
          });
        }
      }
    }

    // Sort by strength and keep top suggestions
    this.connectionSuggestions = suggestions
      .sort((a, b) => b.strength - a.strength)
      .slice(0, 50);
  }

  /**
   * Update regional statistics
   */
  private updateRegionalStats(): void {
    // Define regions
    const regions = [
      { name: 'Northeast', centerLat: 42, centerLon: -73, radius: 300 },
      { name: 'Southeast', centerLat: 33, centerLon: -84, radius: 400 },
      { name: 'Midwest', centerLat: 41, centerLon: -87, radius: 400 },
      { name: 'Southwest', centerLat: 33, centerLon: -112, radius: 400 },
      { name: 'West Coast', centerLat: 37, centerLon: -122, radius: 300 }
    ];

    for (const region of regions) {
      const regionNodes = Array.from(this.nodes.values()).filter(n => {
        const dist = this.calculateDistance(
          n.location,
          { latitude: region.centerLat, longitude: region.centerLon }
        );
        return dist <= region.radius;
      });

      const plantNodes = regionNodes.filter(n => n.type === 'plant');
      const otherNodes = regionNodes.filter(n => n.type !== 'plant');

      const allSpecies = new Set<string>();
      regionNodes.forEach(n => n.species?.forEach(s => allSpecies.add(s)));

      const stats: RegionalStats = {
        region: region.name,
        centerLat: region.centerLat,
        centerLon: region.centerLon,
        nodeCount: otherNodes.length,
        plantCount: plantNodes.length,
        uniqueSpecies: allSpecies.size,
        avgActivityScore: otherNodes.length > 0
          ? Math.round(otherNodes.reduce((sum, n) => sum + n.activityScore, 0) / otherNodes.length)
          : 0,
        connections: otherNodes.reduce((sum, n) => sum + n.connections.length, 0)
      };

      this.regionalStats.set(region.name, stats);
    }
  }

  /**
   * Track network growth
   */
  private trackGrowth(): void {
    const currentNodes = this.nodes.size;
    const currentConnections = Array.from(this.nodes.values())
      .reduce((sum, n) => sum + n.connections.length, 0) / 2;

    const lastGrowth = this.growthHistory[this.growthHistory.length - 1];

    const growth: NetworkGrowth = {
      date: new Date().toISOString(),
      totalNodes: currentNodes,
      totalConnections: Math.round(currentConnections),
      newNodesWeek: lastGrowth ? currentNodes - lastGrowth.totalNodes : currentNodes,
      newConnectionsWeek: lastGrowth ? Math.round(currentConnections) - lastGrowth.totalConnections : Math.round(currentConnections),
      geographicExpansion: this.calculateGeographicExpansion()
    };

    this.growthHistory.push(growth);

    // Keep last year of data
    if (this.growthHistory.length > 52) {
      this.growthHistory = this.growthHistory.slice(-52);
    }
  }

  /**
   * Calculate geographic expansion
   */
  private calculateGeographicExpansion(): number {
    if (this.nodes.size === 0) return 0;

    const nodes = Array.from(this.nodes.values());
    let maxDistance = 0;

    // Find maximum distance between any two nodes (simplified)
    for (let i = 0; i < Math.min(nodes.length, 100); i++) {
      for (let j = i + 1; j < Math.min(nodes.length, 100); j++) {
        const dist = this.calculateDistance(nodes[i].location, nodes[j].location);
        maxDistance = Math.max(maxDistance, dist);
      }
    }

    return Math.round(maxDistance);
  }

  /**
   * Check for network alerts
   */
  private checkNetworkAlerts(): void {
    // Alert for high-potential coverage gaps
    for (const gap of this.coverageGaps) {
      if (gap.populationDensity === 'urban' && gap.distanceToNearest > 50) {
        this.createAlert('warning', 'Coverage Gap in Urban Area',
          gap.recommendation,
          { relatedEntityId: gap.id, relatedEntityType: 'gap' }
        );
      }
    }

    // Alert for network growth milestones
    const nodeCount = this.nodes.size;
    const milestones = [50, 100, 250, 500, 1000];

    for (const milestone of milestones) {
      if (nodeCount >= milestone * 0.95 && nodeCount <= milestone * 1.05) {
        this.createAlert('info', 'Network Milestone',
          `Network has reached approximately ${milestone} participants!`,
          { relatedEntityType: 'network' }
        );
      }
    }
  }

  /**
   * Calculate Haversine distance
   */
  private calculateDistance(
    loc1: { latitude: number; longitude: number },
    loc2: { latitude: number; longitude: number }
  ): number {
    const R = 6371;
    const dLat = (loc2.latitude - loc1.latitude) * Math.PI / 180;
    const dLon = (loc2.longitude - loc1.longitude) * Math.PI / 180;
    const a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
      Math.cos(loc1.latitude * Math.PI / 180) * Math.cos(loc2.latitude * Math.PI / 180) *
      Math.sin(dLon / 2) * Math.sin(dLon / 2);
    const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
    return R * c;
  }

  /**
   * Get network analysis
   */
  getNetworkAnalysis(): NetworkAnalysis {
    return {
      totalNodes: this.nodes.size,
      totalConnections: Array.from(this.nodes.values())
        .reduce((sum, n) => sum + n.connections.length, 0) / 2,
      clusters: this.clusters.map(c => ({
        centroid: { lat: c.centroid.latitude, lon: c.centroid.longitude },
        nodeCount: c.nodes.length,
        avgDistance: c.radius,
        dominantSpecies: c.dominantSpecies
      })),
      hotspots: this.clusters
        .filter(c => c.activityLevel === 'high')
        .map(c => ({
          location: { lat: c.centroid.latitude, lon: c.centroid.longitude },
          intensity: c.nodes.length * c.density,
          type: 'mixed' as const
        })),
      recommendations: this.coverageGaps.map(g => g.recommendation)
    };
  }

  /**
   * Get clusters
   */
  getClusters(): NetworkCluster[] {
    return this.clusters;
  }

  /**
   * Get coverage gaps
   */
  getCoverageGaps(): CoverageGap[] {
    return this.coverageGaps;
  }

  /**
   * Get connection suggestions
   */
  getConnectionSuggestions(): ConnectionSuggestion[] {
    return this.connectionSuggestions;
  }

  /**
   * Get growth history
   */
  getGrowthHistory(): NetworkGrowth[] {
    return this.growthHistory;
  }

  /**
   * Get regional stats
   */
  getRegionalStats(): RegionalStats[] {
    return Array.from(this.regionalStats.values());
  }

  /**
   * Get node by ID
   */
  getNode(nodeId: string): NetworkNode | null {
    return this.nodes.get(nodeId) || null;
  }
}

// Singleton instance
let networkAgentInstance: NetworkDiscoveryAgent | null = null;

export function getNetworkDiscoveryAgent(): NetworkDiscoveryAgent {
  if (!networkAgentInstance) {
    networkAgentInstance = new NetworkDiscoveryAgent();
  }
  return networkAgentInstance;
}
