/*
 * Copyright (C) 2022 StApps
 * This program is free software: you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the Free
 * Software Foundation, version 3.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program. If not, see <https://www.gnu.org/licenses/>.
 */
/* eslint-disable unicorn/no-useless-undefined, @typescript-eslint/no-non-null-assertion */
import {TestBed} from '@angular/core/testing';
import {StorageProvider} from '../../storage/storage.provider';
import {
  InAppReviewProvider,
  IN_APP_REVIEW_COOLDOWN_DAYS,
  IN_APP_REVIEW_NECESSARY_SESSIONS,
  IN_APP_REVIEW_SETTINGS_KEY,
  IN_APP_REVIEW_SESSIONS_KEY,
  IN_APP_REVIEW_LAST_RATING_KEY,
} from './in-app-review.provider';

describe('InappRatingProvider', () => {
  let storageProviderSpy: jasmine.SpyObj<StorageProvider>;
  let inappRatingProvider: InAppReviewProvider;
  const ONE_DAY_IN_MILLIS = 1000 * 3600 * 24;

  beforeEach(async () => {
    storageProviderSpy = jasmine.createSpyObj('StorageProvider', ['init', 'get', 'has', 'put']);

    TestBed.configureTestingModule({
      imports: [],
      providers: [
        InAppReviewProvider,
        {
          provide: StorageProvider,
          useValue: storageProviderSpy,
        },
      ],
    });
    // set settings returned from config
    inappRatingProvider = TestBed.inject(InAppReviewProvider);
    storageProviderSpy.has.and.returnValue(Promise.resolve(false));
  });

  it('should set user sessions count starting with one', async () => {
    storageProviderSpy.get.and.returnValue(Promise.resolve(0));
    const sessionCount = await inappRatingProvider.increaseSessionCount();
    expect(sessionCount).toEqual(1);
  });

  it('should start in app rating/review flow if contitions are met', async () => {
    const nowMinusCooldownDays = Date.now() - (IN_APP_REVIEW_COOLDOWN_DAYS + 1) * ONE_DAY_IN_MILLIS;
    spyOn(inappRatingProvider, 'requestReview').and.returnValue(Promise.resolve());

    storageProviderSpy.get
      .withArgs(`${IN_APP_REVIEW_SETTINGS_KEY}.${IN_APP_REVIEW_SESSIONS_KEY}`)
      .and.returnValue(Promise.resolve(IN_APP_REVIEW_NECESSARY_SESSIONS));
    storageProviderSpy.get
      .withArgs(`${IN_APP_REVIEW_SETTINGS_KEY}.${IN_APP_REVIEW_LAST_RATING_KEY}`)
      .and.returnValue(Promise.resolve(nowMinusCooldownDays));
    await inappRatingProvider.startInAppReviewIfFeasible();
    expect(inappRatingProvider.requestReview).toHaveBeenCalledTimes(1);
  });

  it('should not start in app rating/review flow if contitions are not met', async () => {
    const nowMinusCooldownDays = Date.now() - IN_APP_REVIEW_COOLDOWN_DAYS * ONE_DAY_IN_MILLIS;
    spyOn(inappRatingProvider, 'requestReview').and.returnValue(Promise.resolve());

    storageProviderSpy.get
      .withArgs(`${IN_APP_REVIEW_SETTINGS_KEY}.${IN_APP_REVIEW_SESSIONS_KEY}`)
      .and.returnValue(Promise.resolve(IN_APP_REVIEW_NECESSARY_SESSIONS - 1));
    storageProviderSpy.get
      .withArgs(`${IN_APP_REVIEW_SETTINGS_KEY}.${IN_APP_REVIEW_LAST_RATING_KEY}`)
      .and.returnValue(Promise.resolve(nowMinusCooldownDays));

    await inappRatingProvider.startInAppReviewIfFeasible();

    storageProviderSpy.get
      .withArgs(`${IN_APP_REVIEW_SETTINGS_KEY}.${IN_APP_REVIEW_SESSIONS_KEY}`)
      .and.returnValue(Promise.resolve(IN_APP_REVIEW_NECESSARY_SESSIONS));
    storageProviderSpy.get
      .withArgs(`${IN_APP_REVIEW_SETTINGS_KEY}.${IN_APP_REVIEW_LAST_RATING_KEY}`)
      .and.returnValue(Promise.resolve(nowMinusCooldownDays + ONE_DAY_IN_MILLIS));

    await inappRatingProvider.startInAppReviewIfFeasible();

    expect(inappRatingProvider.requestReview).toHaveBeenCalledTimes(0);
  });
});
