import { Test, TestingModule } from '@nestjs/testing';
import { GrowthPercentileService } from './growth-percentile.service';
import { getRepositoryToken } from '@nestjs/typeorm';
import { Child } from '../../database/entities/child.entity';
import { Activity, ActivityType } from '../../database/entities/activity.entity';
import { Repository } from 'typeorm';

describe('GrowthPercentileService', () => {
  let service: GrowthPercentileService;
  let childRepository: Repository<Child>;
  let activityRepository: Repository<Activity>;

  const mockChild = {
    id: 'child123',
    name: 'Test Child',
    birthDate: new Date('2023-01-01'),
    gender: 'male',
  };

  const mockGrowthActivities = [
    {
      id: '1',
      childId: 'child123',
      type: ActivityType.GROWTH,
      startedAt: new Date('2023-01-01'),
      endedAt: null,
      loggedBy: 'user123',
      metadata: {
        weight: 3.5, // kg
        height: 50, // cm
        headCircumference: 35, // cm
      },
      createdAt: new Date('2023-01-01'),
      updatedAt: new Date('2023-01-01'),
    },
    {
      id: '2',
      childId: 'child123',
      type: ActivityType.GROWTH,
      startedAt: new Date('2023-06-01'),
      endedAt: null,
      loggedBy: 'user123',
      metadata: {
        weight: 7.5,
        height: 65,
        headCircumference: 42,
      },
      createdAt: new Date('2023-06-01'),
      updatedAt: new Date('2023-06-01'),
    },
    {
      id: '3',
      childId: 'child123',
      type: ActivityType.GROWTH,
      startedAt: new Date('2023-12-01'),
      endedAt: null,
      loggedBy: 'user123',
      metadata: {
        weight: 10.0,
        height: 75,
        headCircumference: 46,
      },
      createdAt: new Date('2023-12-01'),
      updatedAt: new Date('2023-12-01'),
    },
  ] as unknown as Activity[];

  beforeEach(async () => {
    const module: TestingModule = await Test.createTestingModule({
      providers: [
        GrowthPercentileService,
        {
          provide: getRepositoryToken(Child),
          useValue: {
            findOne: jest.fn(),
            find: jest.fn(),
          },
        },
        {
          provide: getRepositoryToken(Activity),
          useValue: {
            find: jest.fn(),
            findOne: jest.fn(),
            save: jest.fn(),
          },
        },
      ],
    }).compile();

    service = module.get<GrowthPercentileService>(GrowthPercentileService);
    childRepository = module.get<Repository<Child>>(getRepositoryToken(Child));
    activityRepository = module.get<Repository<Activity>>(
      getRepositoryToken(Activity),
    );
  });

  it('should be defined', () => {
    expect(service).toBeDefined();
  });

  describe('calculateGrowthPercentiles', () => {
    it('should calculate percentiles for a male child', async () => {
      const measurement = {
        date: new Date('2023-06-01'),
        weight: 7.5,
        height: 65,
        headCircumference: 42,
      };

      jest.spyOn(childRepository, 'findOne').mockResolvedValue(mockChild as Child);
      jest.spyOn(activityRepository, 'save').mockResolvedValue({
        id: 'new-measurement',
        childId: 'child123',
        type: ActivityType.GROWTH,
        startedAt: measurement.date,
        endedAt: null,
        loggedBy: 'user123',
        metadata: measurement,
        createdAt: measurement.date,
        updatedAt: measurement.date,
      } as unknown as Activity);

      const result = await service.calculateGrowthPercentiles('child123', measurement);

      expect(result).toHaveProperty('weight');
      expect(result).toHaveProperty('height');
      expect(result).toHaveProperty('headCircumference');

      // Check weight percentile
      if (result.weight) {
        expect(result.weight).toHaveProperty('value');
        expect(result.weight).toHaveProperty('percentile');
        expect(result.weight).toHaveProperty('zScore');
        expect(result.weight.percentile).toBeGreaterThanOrEqual(0);
        expect(result.weight.percentile).toBeLessThanOrEqual(100);
      }
    });

    it('should handle partial measurements', async () => {
      const measurement = {
        date: new Date('2023-06-01'),
        weight: 7.5,
        // No height or head circumference
      };

      jest.spyOn(childRepository, 'findOne').mockResolvedValue(mockChild as Child);
      jest.spyOn(activityRepository, 'save').mockResolvedValue({
        id: 'new-measurement',
        childId: 'child123',
        type: ActivityType.GROWTH,
        startedAt: measurement.date,
        metadata: measurement,
      } as unknown as Activity);

      const result = await service.calculateGrowthPercentiles('child123', measurement);

      expect(result.weight).toBeDefined();
      expect(result.height).toBeUndefined();
      expect(result.headCircumference).toBeUndefined();
    });

    it('should throw error if child not found', async () => {
      jest.spyOn(childRepository, 'findOne').mockResolvedValue(null);

      await expect(
        service.calculateGrowthPercentiles('invalid', {
          date: new Date(),
          weight: 5,
        }),
      ).rejects.toThrow('Child not found');
    });
  });

  describe('analyzeGrowth', () => {
    it('should analyze growth trends and velocity', async () => {
      jest.spyOn(childRepository, 'findOne').mockResolvedValue(mockChild as Child);
      jest.spyOn(activityRepository, 'find').mockResolvedValue(mockGrowthActivities);

      const result = await service.analyzeGrowth('child123');

      expect(result).toHaveProperty('currentPercentiles');
      expect(result).toHaveProperty('growthVelocity');
      expect(result).toHaveProperty('growthCurve');
      expect(result).toHaveProperty('projections');
      expect(result).toHaveProperty('recommendations');
      expect(result).toHaveProperty('alerts');
    });

    it('should detect growth alerts for rapid weight gain', async () => {
      const rapidGrowthActivities = [
        ...mockGrowthActivities,
        {
          id: '4',
          childId: 'child123',
          type: ActivityType.GROWTH,
          startedAt: new Date('2024-01-01'),
          endedAt: null,
          loggedBy: 'user123',
          metadata: {
            weight: 15.0, // Rapid weight gain
            height: 78,
            headCircumference: 47,
          },
          createdAt: new Date('2024-01-01'),
          updatedAt: new Date('2024-01-01'),
        },
      ] as unknown as Activity[];

      jest.spyOn(childRepository, 'findOne').mockResolvedValue(mockChild as Child);
      jest.spyOn(activityRepository, 'find').mockResolvedValue(rapidGrowthActivities);

      const result = await service.analyzeGrowth('child123');

      expect(result.alerts).toBeDefined();
      expect(Array.isArray(result.alerts)).toBe(true);

      // Should have at least one alert
      if (result.alerts.length > 0) {
        expect(result.alerts[0]).toHaveProperty('type');
        expect(result.alerts[0]).toHaveProperty('severity');
        expect(result.alerts[0]).toHaveProperty('message');
      }
    });

    it('should throw error for no measurements', async () => {
      jest.spyOn(childRepository, 'findOne').mockResolvedValue(mockChild as Child);
      jest.spyOn(activityRepository, 'find').mockResolvedValue([]);

      await expect(service.analyzeGrowth('child123'))
        .rejects.toThrow('No growth measurements found');
    });

    it('should calculate correct growth velocity', async () => {
      const twoMeasurements = mockGrowthActivities.slice(0, 2);
      jest.spyOn(childRepository, 'findOne').mockResolvedValue(mockChild as Child);
      jest.spyOn(activityRepository, 'find').mockResolvedValue(twoMeasurements);

      const result = await service.analyzeGrowth('child123');

      expect(result.growthVelocity).toBeDefined();
      expect(result.growthVelocity).toHaveProperty('weightVelocity');
      expect(result.growthVelocity).toHaveProperty('heightVelocity');

      // Weight velocity should be positive (gaining weight)
      if (result.growthVelocity.weightVelocity) {
        expect(result.growthVelocity.weightVelocity.value).toBeGreaterThan(0);
      }

      // Height velocity should be positive (growing taller)
      if (result.growthVelocity.heightVelocity) {
        expect(result.growthVelocity.heightVelocity.value).toBeGreaterThan(0);
      }
    });

    it('should provide age-appropriate recommendations', async () => {
      // Test for newborn (0-3 months)
      const newbornChild = { ...mockChild, birthDate: new Date(Date.now() - 30 * 24 * 60 * 60 * 1000) };
      jest.spyOn(childRepository, 'findOne').mockResolvedValue(newbornChild as Child);
      jest.spyOn(activityRepository, 'find').mockResolvedValue([
        {
          ...mockGrowthActivities[0],
          startedAt: new Date(),
        },
      ] as Activity[]);

      const result = await service.analyzeGrowth('child123');

      expect(result.recommendations).toBeDefined();
      expect(Array.isArray(result.recommendations)).toBe(true);
      expect(result.recommendations.length).toBeGreaterThan(0);
    });
  });


});