/**
 * PlantLineageAgent Tests
 * Tests for the plant lineage tracking agent
 */

import { PlantLineageAgent, getPlantLineageAgent } from '../../../lib/agents/PlantLineageAgent';

// Mock the blockchain manager
jest.mock('../../../lib/blockchain/manager', () => ({
  getBlockchain: jest.fn(() => ({
    getChain: jest.fn(() => [
      // Genesis block
      {
        index: 0,
        timestamp: '2024-01-01T00:00:00Z',
        plant: { id: 'genesis' },
        previousHash: '0',
        hash: 'genesis-hash',
        nonce: 0,
      },
      // First generation plant
      {
        index: 1,
        timestamp: '2024-01-02T00:00:00Z',
        plant: {
          id: 'plant-1',
          name: 'Original Tomato',
          species: 'Tomato',
          variety: 'Cherry',
          generation: 1,
          propagationType: 'original',
          parentPlantId: undefined,
          childPlants: ['plant-2'],
          status: 'thriving',
          dateAcquired: '2024-01-02',
          location: { latitude: 40.7128, longitude: -74.006 },
          environment: { light: 'full_sun' },
          growthMetrics: { height: 50 },
        },
        previousHash: 'genesis-hash',
        hash: 'hash-1',
        nonce: 1,
      },
      // Second generation plant
      {
        index: 2,
        timestamp: '2024-01-15T00:00:00Z',
        plant: {
          id: 'plant-2',
          name: 'Cloned Tomato',
          species: 'Tomato',
          variety: 'Cherry',
          generation: 2,
          propagationType: 'cutting',
          parentPlantId: 'plant-1',
          childPlants: ['plant-3'],
          status: 'healthy',
          dateAcquired: '2024-01-15',
          location: { latitude: 40.73, longitude: -73.99 },
          environment: { light: 'partial_sun' },
          growthMetrics: { height: 30 },
        },
        previousHash: 'hash-1',
        hash: 'hash-2',
        nonce: 2,
      },
      // Third generation plant
      {
        index: 3,
        timestamp: '2024-02-01T00:00:00Z',
        plant: {
          id: 'plant-3',
          name: 'Third Gen Tomato',
          species: 'Tomato',
          variety: 'Cherry',
          generation: 3,
          propagationType: 'seed',
          parentPlantId: 'plant-2',
          childPlants: [],
          status: 'healthy',
          dateAcquired: '2024-02-01',
          location: { latitude: 40.75, longitude: -73.98 },
          environment: { light: 'full_sun' },
          growthMetrics: { height: 20 },
        },
        previousHash: 'hash-2',
        hash: 'hash-3',
        nonce: 3,
      },
    ]),
  })),
}));

describe('PlantLineageAgent', () => {
  let agent: PlantLineageAgent;

  beforeEach(() => {
    agent = new PlantLineageAgent();
  });

  afterEach(async () => {
    if (agent.status === 'running') {
      await agent.stop();
    }
  });

  describe('Initialization', () => {
    it('should initialize with correct config', () => {
      expect(agent.config.id).toBe('plant-lineage-agent');
      expect(agent.config.name).toBe('Plant Lineage Agent');
      expect(agent.config.priority).toBe('high');
      expect(agent.config.intervalMs).toBe(60000);
    });

    it('should start in idle status', () => {
      expect(agent.status).toBe('idle');
    });
  });

  describe('runOnce', () => {
    it('should complete a scan cycle', async () => {
      const result = await agent.runOnce();
      expect(result).not.toBeNull();
      expect(result?.status).toBe('completed');
      expect(result?.type).toBe('lineage_scan');
    });

    it('should scan plants and update cache', async () => {
      await agent.runOnce();
      expect(agent.getLineageAnalysis('plant-1')).not.toBeNull();
      expect(agent.getLineageAnalysis('plant-2')).not.toBeNull();
      expect(agent.getLineageAnalysis('plant-3')).not.toBeNull();
    });

    it('should return scan statistics', async () => {
      const result = await agent.runOnce();
      expect(result?.result).toHaveProperty('plantsScanned');
      expect(result?.result).toHaveProperty('anomaliesFound');
      expect(result?.result).toHaveProperty('cacheSize');
      expect(result?.result.plantsScanned).toBe(3);
    });
  });

  describe('Lineage Analysis', () => {
    beforeEach(async () => {
      await agent.runOnce();
    });

    it('should find ancestors correctly', () => {
      const analysis = agent.getLineageAnalysis('plant-3');
      expect(analysis?.ancestors).toContain('plant-2');
      expect(analysis?.ancestors).toContain('plant-1');
      expect(analysis?.ancestors.length).toBe(2);
    });

    it('should find descendants correctly', () => {
      const analysis = agent.getLineageAnalysis('plant-1');
      expect(analysis?.descendants).toContain('plant-2');
      expect(analysis?.descendants).toContain('plant-3');
    });

    it('should track generation depth', () => {
      const analysis1 = agent.getLineageAnalysis('plant-1');
      const analysis3 = agent.getLineageAnalysis('plant-3');
      expect(analysis1?.generation).toBe(1);
      expect(analysis3?.generation).toBe(3);
    });

    it('should calculate lineage size', () => {
      const analysis = agent.getLineageAnalysis('plant-2');
      // plant-2 has 1 ancestor (plant-1) and 1 descendant (plant-3) + itself = 3
      expect(analysis?.totalLineageSize).toBe(3);
    });

    it('should build propagation chain', () => {
      const analysis = agent.getLineageAnalysis('plant-3');
      expect(analysis?.propagationChain).toEqual(['original', 'cutting', 'seed']);
    });

    it('should calculate health score', () => {
      const analysis = agent.getLineageAnalysis('plant-1');
      expect(analysis?.healthScore).toBeGreaterThan(0);
      expect(analysis?.healthScore).toBeLessThanOrEqual(100);
    });

    it('should return null for non-existent plant', () => {
      const analysis = agent.getLineageAnalysis('non-existent');
      expect(analysis).toBeNull();
    });
  });

  describe('Network Statistics', () => {
    beforeEach(async () => {
      await agent.runOnce();
    });

    it('should calculate total plants', () => {
      const stats = agent.getNetworkStats();
      expect(stats.totalPlants).toBe(3);
    });

    it('should calculate total lineages (root plants)', () => {
      const stats = agent.getNetworkStats();
      expect(stats.totalLineages).toBe(1); // Only plant-1 has no ancestors
    });

    it('should calculate average generation depth', () => {
      const stats = agent.getNetworkStats();
      // (1 + 2 + 3) / 3 = 2
      expect(stats.avgGenerationDepth).toBe(2);
    });

    it('should return empty stats when no data', () => {
      const emptyAgent = new PlantLineageAgent();
      const stats = emptyAgent.getNetworkStats();
      expect(stats.totalPlants).toBe(0);
      expect(stats.totalLineages).toBe(0);
    });
  });

  describe('Anomaly Detection', () => {
    it('should return empty anomalies initially', () => {
      expect(agent.getAnomalies()).toEqual([]);
    });

    it('should detect anomalies during scan', async () => {
      await agent.runOnce();
      const anomalies = agent.getAnomalies();
      // The mock data is valid, so no anomalies should be detected
      expect(Array.isArray(anomalies)).toBe(true);
    });
  });

  describe('Singleton', () => {
    it('should return same instance from getPlantLineageAgent', () => {
      const agent1 = getPlantLineageAgent();
      const agent2 = getPlantLineageAgent();
      expect(agent1).toBe(agent2);
    });
  });
});
