/*
 * 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/>.
 */
import {Injectable} from '@angular/core';
import {ConfigProvider} from '../config/config.provider';
import {SCAssessment, SCUuid} from '@openstapps/core';
import {DefaultAuthService} from '../auth/default-auth.service';
import {HttpClient} from '@angular/common/http';
import {uniqBy, keyBy} from '@openstapps/collection-utils';

/**
 *
 */
export function toAssessmentMap(data: SCAssessment[]): Record<SCUuid, SCAssessment> {
  return keyBy(
    uniqBy(
      [
        ...data,
        ...data.flatMap<SCAssessment>(
          assessment =>
            [...(assessment.superAssessments ?? [])]
              .reverse()
              .map<SCAssessment>((superAssessment, index, array) => {
                const superAssessmentCopy = {
                  ...superAssessment,
                } as SCAssessment;
                superAssessmentCopy.origin = assessment.origin;
                superAssessmentCopy.superAssessments = array.slice(index + 1).reverse();
                return superAssessmentCopy;
              }) ?? [],
        ),
      ] as SCAssessment[],
      it => it.uid,
    ),
    it => it.uid,
  );
}

@Injectable({
  providedIn: 'root',
})
export class AssessmentsProvider {
  assessmentPath = 'assessments';

  // usually this wouldn't be necessary, but the assessment service
  // is very aggressive about too many requests being made to the server
  cache?: Promise<SCAssessment[]>;

  assessments: Promise<Record<SCUuid, SCAssessment>>;

  cacheTimestamp = 0;

  // 15 minutes
  cacheMaxAge = 15 * 60 * 1000;

  constructor(
    readonly configProvider: ConfigProvider,
    readonly defaultAuth: DefaultAuthService,
    readonly http: HttpClient,
  ) {}

  async getAssessment(uid: SCUuid, accessToken?: string | null, forceFetch = false): Promise<SCAssessment> {
    await this.getAssessments(accessToken, forceFetch);

    return (await this.assessments)[uid];
  }

  async getAssessments(accessToken?: string | null, forceFetch = false): Promise<SCAssessment[]> {
    // again, this is a hack to get around the fact that the assessment service
    // is very aggressive how many requests you can make, so it can happen
    // during development that simply by reloading pages over and over again
    // the assessment service will block you
    if (accessToken === 'mock' && !this.cache) {
      this.cacheTimestamp = Date.now();
      this.cache = import('./assessment-mock-data.json').then(it => it.data as SCAssessment[]);
      this.assessments = this.cache.then(toAssessmentMap);
    }

    if (this.cache && !forceFetch && Date.now() - this.cacheTimestamp < this.cacheMaxAge) {
      return this.cache;
    }

    const url = this.configProvider.config.app.features.extern?.hisometry.url;
    if (!url) throw new Error('Config lacks url for hisometry');

    this.cache = this.http
      .get<{data: SCAssessment[]}>(`${url}/${this.assessmentPath}`, {
        headers: {
          Authorization: `Bearer ${accessToken ?? (await this.defaultAuth.getValidToken()).accessToken}`,
        },
      })
      .toPromise()
      .then(it => {
        this.cacheTimestamp = Date.now();

        return it?.data ?? [];
      });
    this.assessments = this.cache.then(toAssessmentMap);

    return this.cache;
  }
}
