/**
 * Unit tests for geotemporal grid downsampling strategy
 */

import { createGeotemporalGridDownsampler } from '../../../../src/downsampling/strategies/geotemporal-grid';
import { GeotemporalGridStrategy } from '../../../../src/types';
import { 
  createWeatherDataGrid,
  createWeatherTimeSeries,
  createStormWeatherDataPoint
} from '../../../fixtures/weather-data';
import {
  createSeaStateDataGrid,
  createSeaStateTimeSeries,
  createRoughSeaStateDataPoint
} from '../../../fixtures/sea-state-data';

describe('GeotemporalGridDownsampler', () => {
  const downsampler = createGeotemporalGridDownsampler();
  
  // Test strategy configuration
  const weatherStrategy: GeotemporalGridStrategy = {
    type: 'geotemporal-grid',
    baseGridSize: 1.0,
    adaptiveGridLevels: 3,
    coastalResolutionBoost: 2.0,
    temporalHierarchy: {
      recent: { maxAge: 7, resolution: 1 },        // 7 days, 1 hour resolution
      mediumTerm: { maxAge: 30, resolution: 6 },   // 30 days, 6 hour resolution
      longTerm: { maxAge: 90, resolution: 24 },    // 90 days, 1 day resolution
      historical: { maxAge: 365, resolution: 168 }, // 365 days, 1 week resolution
      seasonal: { resolution: 720 }                // 1 month resolution
    },
    vesselPosition: { latitude: 37.7749, longitude: -122.4194 },
    criticalFeatureThresholds: {
      pressureChangeRate: 1.0,  // 1 mb/hour
      windSpeed: 25,            // 25 knots
      waveHeight: 2.5,          // 2.5 meters
      temperatureGradient: 5.0  // 5°C per degree
    }
  };
  
  const seaStateStrategy: GeotemporalGridStrategy = {
    type: 'geotemporal-grid',
    baseGridSize: 0.5,  // Higher base resolution for sea state
    adaptiveGridLevels: 3,
    temporalHierarchy: {
      recent: { maxAge: 3, resolution: 1 },        // 3 days, 1 hour resolution
      mediumTerm: { maxAge: 14, resolution: 3 },   // 14 days, 3 hour resolution
      longTerm: { maxAge: 60, resolution: 12 },    // 60 days, 12 hour resolution
      historical: { maxAge: 180, resolution: 72 }, // 180 days, 3 day resolution
      seasonal: { resolution: 720 }                // 1 month resolution
    },
    criticalFeatureThresholds: {
      waveHeight: 2.0,          // 2.0 meters
      temperatureGradient: 3.0  // 3°C per degree (for currents)
    }
  };
  
  describe('type property', () => {
    it('should return the correct type', () => {
      expect(downsampler.type).toBe('geotemporal-grid');
    });
  });
  
  describe('downsample method', () => {
    it('should return original data if less than 3 points are provided', async () => {
      const data = createWeatherTimeSeries(37.7749, -122.4194, 2, 3600000, new Date());
      const result = await downsampler.downsample(data, weatherStrategy);
      expect(result).toEqual(data);
    });
    
    it('should downsample weather data', async () => {
      const data = createWeatherDataGrid(37.7749, -122.4194, 1.0, 10, new Date());
      const result = await downsampler.downsample(data, weatherStrategy);
      
      // Result should be smaller than original data
      expect(result.length).toBeLessThan(data.length);
      
      // Result should have downsampling metadata
      expect(result[0].metadata.tags.downsampled).toBe('true');
      expect(result[0].metadata.tags.downsamplingStrategy).toBe('geotemporal-grid');
      expect(result[0].metadata.tags.downsampledFrom).toBe(data.length.toString());
      
      // Result should have grid cell information
      expect(result[0].metadata.tags.gridCell).toBeDefined();
      expect(result[0].metadata.tags.gridLevel).toBeDefined();
      expect(result[0].metadata.tags.bucketResolution).toBeDefined();
      expect(result[0].metadata.tags.pointCount).toBeDefined();
    });
    
    it('should downsample sea state data', async () => {
      const data = createSeaStateDataGrid(37.8, -122.5, 0.5, 10, new Date());
      const result = await downsampler.downsample(data, seaStateStrategy);
      
      // Result should be smaller than original data
      expect(result.length).toBeLessThan(data.length);
      
      // Result should have downsampling metadata
      expect(result[0].metadata.tags.downsampled).toBe('true');
      expect(result[0].metadata.tags.downsamplingStrategy).toBe('geotemporal-grid');
      expect(result[0].metadata.tags.downsampledFrom).toBe(data.length.toString());
    });
    
    it('should preserve critical weather features', async () => {
      // Create weather data grid
      const baseData = createWeatherDataGrid(37.7749, -122.4194, 1.0, 10, new Date());
      
      // Add a storm point with wind speed above threshold
      const stormPoint = createStormWeatherDataPoint(37.7749, -122.4194, new Date().toISOString());
      const data = [...baseData, stormPoint];
      
      const result = await downsampler.downsample(data, weatherStrategy);
      
      // Find the storm point in the result
      const resultStormPoint = result.find(point => 
        point.data.windSpeed >= weatherStrategy.criticalFeatureThresholds!.windSpeed!
      );
      
      // Storm point should be preserved
      expect(stormPoint).toBeDefined();
      expect(stormPoint!.data.windSpeed).toBeGreaterThanOrEqual(weatherStrategy.criticalFeatureThresholds!.windSpeed!);
    });
    
    it('should preserve critical sea state features', async () => {
      // Create sea state data grid
      const baseData = createSeaStateDataGrid(37.8, -122.5, 0.5, 10, new Date());
      
      // Add a rough sea state point with high waves
      const roughPoint = createRoughSeaStateDataPoint(37.8, -122.5, new Date().toISOString());
      const data = [...baseData, roughPoint];
      
      const result = await downsampler.downsample(data, seaStateStrategy);
      
      // Find the high wave point in the result
      const highWavePoint = result.find(point => 
        point.data.waveHeight >= seaStateStrategy.criticalFeatureThresholds!.waveHeight!
      );
      
      // High wave point should be preserved
      expect(highWavePoint).toBeDefined();
      expect(highWavePoint!.data.waveHeight).toBeGreaterThanOrEqual(seaStateStrategy.criticalFeatureThresholds!.waveHeight!);
    });
    
    it('should create higher resolution grid cells near vessel position', async () => {
      const data = createWeatherDataGrid(37.7749, -122.4194, 2.0, 15, new Date());
      const result = await downsampler.downsample(data, weatherStrategy);
      
      // Get grid levels
      const gridLevels = result.map(point => parseInt(point.metadata.tags.gridLevel));
      
      // Should have multiple grid levels
      const uniqueLevels = new Set(gridLevels);
      expect(uniqueLevels.size).toBeGreaterThan(1);
      
      // Should have at least one high-resolution cell (level > 0)
      expect(Math.max(...gridLevels)).toBeGreaterThan(0);
    });
    
    it('should handle errors gracefully', async () => {
      // Create weather data
      const data = createWeatherTimeSeries(37.7749, -122.4194, 10, 3600000, new Date());
      
      // Remove coordinates to make it invalid
      data.forEach(item => {
        delete item.data.latitude;
        delete item.data.longitude;
      });
      
      // Should return original data on error
      const result = await downsampler.downsample(data, weatherStrategy);
      expect(result).toEqual(data);
    });
  });
});
