import { describe, it, expect, vi, beforeEach } from 'vitest';
import { NutritionTargetService } from '../../../src/modules/nutrition-targets/nutrition-target.service.js';
import type { NutritionTargetRepository } from '../../../src/modules/nutrition-targets/nutrition-target.repository.js';

describe(NutritionTargetService.name, () => {
  let service: NutritionTargetService;
  let mockRepo: { [K in keyof NutritionTargetRepository]: ReturnType<typeof vi.fn> };

  beforeEach(() => {
    mockRepo = {
      findByUser: vi.fn(),
      findAllByUser: vi.fn(),
      create: vi.fn(),
      deactivateAllForUser: vi.fn(),
      update: vi.fn(),
    } as never;

    service = new NutritionTargetService({
      nutritionTargetRepository: mockRepo as unknown as NutritionTargetRepository,
    });
  });

  describe('getActiveByUser', () => {
    it('delegates to repository', async () => {
      const mockTarget = { dailyCalories: 2000, isActive: true };
      mockRepo.findByUser.mockResolvedValue(mockTarget);

      const result = await service.getActiveByUser('u1', 'h1');
      expect(mockRepo.findByUser).toHaveBeenCalledWith('u1', 'h1');
      expect(result).toEqual(mockTarget);
    });
  });

  describe('getAllByUser', () => {
    it('delegates to repository', async () => {
      const mockTargets = [{ dailyCalories: 2000 }, { dailyCalories: 1800 }];
      mockRepo.findAllByUser.mockResolvedValue(mockTargets);

      const result = await service.getAllByUser('u1', 'h1');
      expect(mockRepo.findAllByUser).toHaveBeenCalledWith('u1', 'h1');
      expect(result).toEqual(mockTargets);
    });
  });

  describe('setTarget', () => {
    it('deactivates existing targets before creating an active one', async () => {
      const mockInput = { dailyCalories: 2000, proteinG: 100, carbsG: 200, fatG: 50, isActive: true };
      const createdTarget = { ...mockInput, _id: 'new-id', userId: 'u1', householdId: 'h1' };
      mockRepo.create.mockResolvedValue(createdTarget);

      const result = await service.setTarget('u1', 'h1', mockInput);

      expect(mockRepo.deactivateAllForUser).toHaveBeenCalledWith('u1', 'h1');
      expect(mockRepo.create).toHaveBeenCalledWith({
        ...mockInput,
        userId: 'u1',
        householdId: 'h1',
      });
      expect(result).toEqual(createdTarget);
    });

    it('does NOT deactivate others if isActive is explicitly false', async () => {
      const mockInput = { dailyCalories: 2000, proteinG: 100, carbsG: 200, fatG: 50, isActive: false };
      await service.setTarget('u1', 'h1', mockInput);

      expect(mockRepo.deactivateAllForUser).not.toHaveBeenCalled();
      expect(mockRepo.create).toHaveBeenCalled();
    });
  });

  describe('calculatePreset', () => {
    it('calculates macros correctly for maintenance (30p / 40c / 30f)', () => {
      const result = service.calculatePreset(2000, 'maintenance');
      
      // Math:
      // Protein: (2000 * 0.3) / 4 = 600 / 4 = 150
      // Carbs: (2000 * 0.4) / 4 = 800 / 4 = 200
      // Fat: (2000 * 0.3) / 9 = 600 / 9 = 66.66 => 67
      expect(result).toEqual({
        dailyCalories: 2000,
        proteinG: 150,
        carbsG: 200,
        fatG: 67,
        isActive: true,
      });
    });

    it('calculates macros correctly for loss (40p / 30c / 30f)', () => {
      const result = service.calculatePreset(2000, 'loss');
      
      // Math:
      // Protein: (2000 * 0.4) / 4 = 800 / 4 = 200
      // Carbs: (2000 * 0.3) / 4 = 600 / 4 = 150
      // Fat: (2000 * 0.3) / 9 = 600 / 9 = 66.66 => 67
      expect(result).toEqual({
        dailyCalories: 2000,
        proteinG: 200,
        carbsG: 150,
        fatG: 67,
        isActive: true,
      });
    });

    it('calculates macros correctly for gain (25p / 50c / 25f)', () => {
      const result = service.calculatePreset(2000, 'gain');
      
      // Math:
      // Protein: (2000 * 0.25) / 4 = 500 / 4 = 125
      // Carbs: (2000 * 0.5) / 4 = 1000 / 4 = 250
      // Fat: (2000 * 0.25) / 9 = 500 / 9 = 55.55 => 56
      expect(result).toEqual({
        dailyCalories: 2000,
        proteinG: 125,
        carbsG: 250,
        fatG: 56,
        isActive: true,
      });
    });
  });
});
