/*
 * Copyright (C) 2023 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/>.
 */
import {Injectable} from '@angular/core';
import {
  SCAcademicPriceGroup,
  SCDish,
  SCISO8601Date,
  SCRatingRequest,
  SCRatingResponse,
  SCRatingRoute,
  SCUserGroup,
  SCUserGroupSetting,
  SCUuid,
} from '@openstapps/core';
import {StAppsWebHttpClient} from './stapps-web-http-client.provider';
import {StorageProvider} from '../storage/storage.provider';
import {Client} from '@openstapps/api';
import {environment} from '../../../environments/environment';
import {SettingsProvider} from '../settings/settings.provider';

interface RatingStorage {
  date: SCISO8601Date;
  uuids: Record<SCUuid, true>;
}

@Injectable({
  providedIn: 'root',
})
export class RatingProvider {
  private readonly client: Client;

  private readonly backendUrl = environment.backend_url;

  private readonly appVersion = environment.backend_version;

  private readonly ratingRoute = new SCRatingRoute();

  private readonly storageKey = 'rating.rated_uuids';

  constructor(
    stAppsWebHttpClient: StAppsWebHttpClient,
    private readonly storageProvider: StorageProvider,
    private readonly settingsProvider: SettingsProvider,
  ) {
    this.client = new Client(stAppsWebHttpClient, this.backendUrl, this.appVersion);
  }

  private get today() {
    const today = new Date();
    return new Date(today.getFullYear(), today.getMonth(), today.getDate()).toISOString();
  }

  private get userGroup(): Promise<SCUserGroup> {
    return this.settingsProvider
      .getSetting('profile', 'group')
      .then(it => (it as SCUserGroupSetting).value as SCUserGroup);
  }

  private async getStoredRatings(): Promise<RatingStorage> {
    const has = await this.storageProvider.has(this.storageKey);
    const current = has
      ? await this.storageProvider.get<RatingStorage | undefined>(this.storageKey)
      : undefined;
    const expired = current?.date !== this.today;
    return expired
      ? await this.storageProvider.put<RatingStorage>(this.storageKey, {
          date: this.today,
          uuids: {},
        })
      : current!;
  }

  private async setStoredRatings(value: RatingStorage | undefined) {
    await (value ? this.storageProvider.put(this.storageKey, value) : this.storageProvider.delete());
  }

  private async markAsRated(uid: SCUuid) {
    const rating = await this.getStoredRatings();
    await this.setStoredRatings({
      ...rating,
      uuids: {...rating.uuids, [uid]: true},
    });
  }

  async rate(uid: SCUuid, rating: SCRatingRequest['rating']) {
    const request: SCRatingRequest = {
      rating: rating,
      userGroup: await this.userGroup,
      uid,
    };
    const response = await this.client.invokeRoute<SCRatingResponse>(this.ratingRoute, undefined, request);

    await this.markAsRated(uid);

    return response;
  }

  async canRate(dish: SCDish): Promise<boolean> {
    const userGroup = (
      {
        students: 'student',
        employees: 'employee',
        guests: 'guest',
      } as Record<SCUserGroup, keyof SCAcademicPriceGroup>
    )[await this.userGroup];
    return dish.offers?.find(it => it.prices?.[userGroup]) !== undefined;
  }

  async hasRated(uid: SCUuid): Promise<boolean> {
    const ratings = await this.getStoredRatings();
    return ratings.uuids[uid] ?? false;
  }
}
