import {Injectable} from '@angular/core';
import {InAppReview} from '@capacitor-community/in-app-review';
import {StorageProvider} from '../../storage/storage.provider';

export const IN_APP_REVIEW_COOLDOWN_DAYS = 365;
export const IN_APP_REVIEW_NECESSARY_SESSIONS = 3;

export const IN_APP_REVIEW_SETTINGS_KEY = 'inapp-rating';
export const IN_APP_REVIEW_SESSIONS_KEY = 'sessions';
export const IN_APP_REVIEW_LAST_RATING_KEY = 'last-rating';
export type IN_APP_REVIEW_KEYS = typeof IN_APP_REVIEW_SESSIONS_KEY | typeof IN_APP_REVIEW_LAST_RATING_KEY;

/**
 * Provider for In-App Review
 */
@Injectable()
export class InAppReviewProvider {
  requestReview: () => Promise<void>;

  /**
   * @param storageProvider TODO
   */
  constructor(private readonly storageProvider: StorageProvider) {
    this.requestReview = InAppReview.requestReview;
  }

  /**
   *
   */
  private inappRatingSettingStorageKey(key: string): string {
    return `${IN_APP_REVIEW_SETTINGS_KEY}.${key}`;
  }

  /**
   *
   */
  private async getInAppReviewSetting<T>(
    key: IN_APP_REVIEW_KEYS,
    defaultValue: T,
  ): Promise<typeof defaultValue> {
    try {
      return await this.storageProvider.get<typeof defaultValue>(this.inappRatingSettingStorageKey(key));
    } catch {
      return defaultValue;
    }
  }

  /**
   *
   */
  private async setInAppReviewSetting<T>(key: IN_APP_REVIEW_KEYS, value: T) {
    return this.storageProvider.put<typeof value>(this.inappRatingSettingStorageKey(key), value);
  }

  /**
   *
   */
  private async setInAppReviewSessions(value: number) {
    return this.setInAppReviewSetting(IN_APP_REVIEW_SESSIONS_KEY, value);
  }

  /**
   *
   */
  private async getInAppReviewSessions(): Promise<number> {
    return this.getInAppReviewSetting(IN_APP_REVIEW_SESSIONS_KEY, 0);
  }

  /**
   *
   */
  private async setInAppReviewLastRating(value: Date) {
    return this.setInAppReviewSetting(IN_APP_REVIEW_LAST_RATING_KEY, value.getTime());
  }

  /**
   *
   */
  private async getInAppReviewLastRating(): Promise<Date> {
    return this.getInAppReviewSetting(IN_APP_REVIEW_LAST_RATING_KEY, 0).then(timestamp => {
      return new Date(timestamp);
    });
  }

  /**
   * Increases session count to keep local info how often the app was used.
   */
  public async increaseSessionCount(increment = 1): Promise<number> {
    try {
      const currentSessions = await this.getInAppReviewSessions();
      await this.setInAppReviewSessions(currentSessions + increment);
      return currentSessions + increment;
    } catch {
      return -1;
    }
  }

  /**
   * Invokes In App Review Flow/Views depending on the OS iff conditions are met.
   */
  public async startInAppReviewIfFeasible(): Promise<boolean> {
    try {
      const currentSessions = await this.getInAppReviewSessions();
      const lastRating = await this.getInAppReviewLastRating();
      const dateDiffMillis = Math.abs(lastRating.getTime() - Date.now());
      const dateDiffDays = Math.floor(dateDiffMillis / (1000 * 3600 * 24));

      if (currentSessions < IN_APP_REVIEW_NECESSARY_SESSIONS || dateDiffDays < IN_APP_REVIEW_COOLDOWN_DAYS) {
        return false;
      }

      await this.requestReview();
      await this.setInAppReviewLastRating(new Date());

      return true;
    } catch {
      return false;
    }
  }
}
