const { expect } = require('chai');
const sinon = require('sinon');
const proxyquire = require('proxyquire');
const fs = require('fs').promises;

// models의 각 모델을 미리 stub
const mockModels = {
  EventScore: { findAll: sinon.stub() },
  Member: { findByPk: sinon.stub() },
  Club: { findByPk: sinon.stub() }
};

// proxyquire로 averageService를 models stub과 함께 불러오기
let averageService;


// averageService를 require (이 시점에 models가 이미 stub됨)
describe('averageService', () => {
  let sandbox;
  let freshAverageService;

  beforeEach(() => {
    sandbox = sinon.createSandbox();
    mockModels.EventScore.findAll.reset();
    mockModels.Member.findByPk.reset();
    mockModels.Club.findByPk.reset();
    freshAverageService = proxyquire.noCallThru().load('../../services/averageService', {
      '../models': mockModels
    });
  });

  afterEach(() => {
    sandbox.restore();
  });

  describe('calculateMemberAverage', () => {
    it('점수가 없을 경우 기본값 0을 반환해야 함', async () => {
      sandbox.stub(fs, 'mkdir').resolves();
      sandbox.stub(fs, 'writeFile').resolves();
      sandbox.stub(fs, 'readFile').rejects({ code: 'ENOENT' });
      mockModels.EventScore.findAll.resolves([]);
      sandbox.stub(freshAverageService, 'getFromCache').resolves(null);
      sandbox.stub(freshAverageService, 'setToCache').resolves(true);
      sandbox.stub(freshAverageService, 'invalidateCache').resolves(true);
      const startDate = new Date('2023-01-01');
      const endDate = new Date('2023-12-31');
      const result = await freshAverageService.calculateMemberAverage(1, startDate, endDate);
      expect(result).to.deep.equal({ average: 0, games: 0, totalScore: 0 });
      expect(mockModels.EventScore.findAll.called).to.be.true;
    });

    it('점수가 있을 경우 올바른 평균을 계산해야 함', async () => {
      sandbox.stub(fs, 'mkdir').resolves();
      sandbox.stub(fs, 'writeFile').resolves();
      sandbox.stub(fs, 'readFile').rejects({ code: 'ENOENT' });
      mockModels.EventScore.findAll.resolves([
        { score: 180 }, { score: 200 }, { score: 220 }
      ]);
      sandbox.stub(freshAverageService, 'getFromCache').resolves(null);
      sandbox.stub(freshAverageService, 'setToCache').resolves(true);
      sandbox.stub(freshAverageService, 'invalidateCache').resolves(true);
      const startDate = new Date('2023-01-01');
      const endDate = new Date('2023-12-31');
      const result = await freshAverageService.calculateMemberAverage(1, startDate, endDate);
      expect(result).to.deep.equal({ average: 200, games: 3, totalScore: 600 });
      expect(mockModels.EventScore.findAll.called).to.be.true;
    });
  });

  describe('calculateMemberAverageByClubSetting', () => {
    it('클럽 설정에 따라 올바른 기간으로 평균을 계산해야 함', async () => {
      sandbox.stub(fs, 'mkdir').resolves();
      sandbox.stub(fs, 'writeFile').resolves();
      sandbox.stub(fs, 'readFile').rejects({ code: 'ENOENT' });
      const calculateStub = sandbox.stub().resolves({
        average: 185, games: 10, totalScore: 1850
      });
      mockModels.Member.findByPk.resolves({ id: 1, name: '테스트 회원' });
      mockModels.Club.findByPk.resolves({ id: 1, name: '테스트 클럽', averageCalculationPeriod: '최근 3개월' });
      const result = await freshAverageService.calculateMemberAverageByClubSetting(1, 1, {
        ...freshAverageService,
        calculateMemberAverage: calculateStub
      });
      expect(result).to.deep.equal({ average: 185, games: 10, totalScore: 1850 });
      expect(mockModels.Member.findByPk.called).to.be.true;
      expect(mockModels.Club.findByPk.called).to.be.true;
      expect(calculateStub.called).to.be.true;
    });
  });

  describe('invalidateMemberAverageCache', () => {
    it('회원 ID에 해당하는 캐시를 무효화해야 함', async () => {
      const invalidateCacheStub = sandbox.stub().resolves(true);
      const result = await freshAverageService.invalidateMemberAverageCache(1, {
        ...freshAverageService,
        invalidateCache: invalidateCacheStub
      });
      expect(invalidateCacheStub.calledWith('average:1')).to.be.true;
      expect(result).to.be.true;
    });
  });
});
