// (C) Copyright 2015 Martin Dougiamas
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

import { Injectable } from '@angular/core';
import { CoreLoggerProvider } from '../../../providers/logger';
import { CoreSite } from '../../../classes/site';
import { CoreSitesProvider } from '../../../providers/sites';
import { CoreUtilsProvider } from '../../../providers/utils/utils';
import { CoreCoursesProvider } from '../../courses/providers/courses';

/**
 * Service to provide grade functionalities.
 */
@Injectable()
export class CoreGradesProvider {
    protected ROOT_CACHE_KEY = 'mmGrades:';

    protected logger;

    constructor(logger: CoreLoggerProvider, private sitesProvider: CoreSitesProvider,
            private coursesProvider: CoreCoursesProvider) {
        this.logger = logger.getInstance('CoreGradesProvider');
    }

    /**
     * Get cache key for grade table data WS calls.
     *
     * @param {number} courseId ID of the course to get the grades from.
     * @param {number} userId   ID of the user to get the grades from.
     * @return {string}         Cache key.
     */
    protected getCourseGradesCacheKey(courseId: number, userId: number): string {
        return this.getCourseGradesPrefixCacheKey(courseId) + userId;
    }

    /**
     * Get cache key for grade table data WS calls.
     *
     * @param {number} courseId     ID of the course to get the grades from.
     * @param {number} userId       ID of the user to get the grades from.
     * @param {number} [groupId]    ID of the group to get the grades from. Default: 0.
     * @return {string}         Cache key.
     */
    protected getCourseGradesItemsCacheKey(courseId: number, userId: number, groupId: number): string {
        groupId = groupId || 0;

        return this.getCourseGradesPrefixCacheKey(courseId) + userId + ':' + groupId;
    }

    /**
     * Get prefix cache key for grade table data WS calls.
     *
     * @param {number} courseId ID of the course to get the grades from.
     * @return {string}         Cache key.
     */
    protected getCourseGradesPrefixCacheKey(courseId: number): string {
        return this.ROOT_CACHE_KEY + 'items:' + courseId + ':';
    }

    /**
     * Get cache key for courses grade WS calls.
     *
     * @return {string}   Cache key.
     */
    protected getCoursesGradesCacheKey(): string {
        return this.ROOT_CACHE_KEY + 'coursesgrades';
    }

    /**
     * Get the grade items for a certain course.
     *
     * @param  {number}  courseId             ID of the course to get the grades from.
     * @param  {number}  [userId]             ID of the user to get the grades from.
     * @param  {number}  [groupId]            ID of the group to get the grades from. Default 0.
     * @param  {string}  [siteId]             Site ID. If not defined, current site.
     * @param  {boolean} [ignoreCache=false]  True if it should ignore cached data (it will always fail in offline or server down).
     * @return {Promise<any>}                      Promise to be resolved when the grades table is retrieved.
     */
    getCourseGradesItems(courseId: number, userId?: number, groupId?: number, siteId?: string,
            ignoreCache: boolean = false): Promise<any> {
        return this.sitesProvider.getSite(siteId).then((site) => {
            userId = userId || site.getUserId();
            groupId = groupId || 0;

            this.logger.debug(`Get grades for course '${courseId}' and user '${userId}'`);

            const data = {
                    courseid : courseId,
                    userid   : userId,
                    groupid  : groupId
                },
                preSets = {
                    cacheKey: this.getCourseGradesItemsCacheKey(courseId, userId, groupId)
                };

            if (ignoreCache) {
                preSets['getFromCache'] = 0;
                preSets['emergencyCache'] = 0;
            }

            return site.read('gradereport_user_get_grade_items', data, preSets).then((grades) => {
                if (grades && grades.usergrades && grades.usergrades[0]) {
                    return grades.usergrades[0].gradeitems;
                }

                return Promise.reject(null);
            });
        });
    }

    /**
     * Get the grades for a certain course.
     * Using gradereport_user_get_grades_table in case is not avalaible.
     *
     * @param  {number}  courseId             ID of the course to get the grades from.
     * @param  {number}  [userId]             ID of the user to get the grades from.
     * @param  {string}  [siteId]             Site ID. If not defined, current site.
     * @param  {boolean} [ignoreCache=false]  True if it should ignore cached data (it will always fail in offline or server down).
     * @return {Promise<any>}                      Promise to be resolved when the grades table is retrieved.
     */
    getCourseGradesTable(courseId: number, userId?: number, siteId?: string, ignoreCache: boolean = false): Promise<any> {
        return this.sitesProvider.getSite(siteId).then((site) => {
            userId = userId || site.getUserId();

            this.logger.debug(`Get grades for course '${courseId}' and user '${userId}'`);

            const data = {
                    courseid : courseId,
                    userid   : userId
                },
                preSets = {
                    cacheKey: this.getCourseGradesCacheKey(courseId, userId)
                };

            if (ignoreCache) {
                preSets['getFromCache'] = 0;
                preSets['emergencyCache'] = 0;
            }

            return site.read('gradereport_user_get_grades_table', data, preSets).then((table) => {
                if (table && table.tables && table.tables[0]) {
                    return table.tables[0];
                }

                return Promise.reject(null);
            });
        });
    }

    /**
     * Get the grades for a certain course.
     *
     * @param {string} [siteId] Site ID. If not defined, current site.
     * @return {Promise<any>}   Promise to be resolved when the grades are retrieved.
     */
    getCoursesGrades(siteId?: string): Promise<any> {
        return this.sitesProvider.getSite(siteId).then((site) => {
            this.logger.debug('Get course grades');

            const preSets = {
                cacheKey: this.getCoursesGradesCacheKey()
            };

            return site.read('gradereport_overview_get_course_grades', undefined, preSets).then((data) => {
                if (data && data.grades) {
                    return data.grades;
                }

                return Promise.reject(null);
            });
        });
    }

    /**
     * Invalidates grade table data WS calls.
     *
     * @param {number} courseId Course ID.
     * @param {number} [userId]   User ID.
     * @param {string} [siteId]   Site id (empty for current site).
     * @return {Promise<any>}        Promise resolved when the data is invalidated.
     */
    invalidateCourseGradesData(courseId: number, userId?: number, siteId?: string): Promise<any> {
        return this.sitesProvider.getSite(siteId).then((site) => {
            userId = userId || site.getUserId();

            return site.invalidateWsCacheForKey(this.getCourseGradesCacheKey(courseId, userId));
        });
    }

    /**
     * Invalidates courses grade data WS calls.
     *
     * @param {string} [siteId]   Site id (empty for current site).
     * @return {Promise<any>}     Promise resolved when the data is invalidated.
     */
    invalidateCoursesGradesData(siteId?: string): Promise<any> {
        return this.sitesProvider.getSite(siteId).then((site) => {
            return site.invalidateWsCacheForKey(this.getCoursesGradesCacheKey());
        });
    }

    /**
     * Returns whether or not the plugin is enabled for a certain site.
     *
     * @param  {string} [siteId] Site ID. If not defined, current site.
     * @return {Promise<boolean>}  Resolve with true if plugin is enabled, false otherwise.
     * @since  Moodle 3.2
     */
    isCourseGradesEnabled(siteId?: string): Promise<boolean> {
        return this.sitesProvider.getSite(siteId).then((site) => {
            if (!site.wsAvailable('gradereport_overview_get_course_grades')) {
                return false;
            }
            // Now check that the configurable mygradesurl is pointing to the gradereport_overview plugin.
            const url = site.getStoredConfig('mygradesurl') || '';

            return url.indexOf('/grade/report/overview/') !== -1;
        });
    }

    /**
     * Returns whether or not the grade addon is enabled for a certain course.
     *
     * @param {number} courseId  Course ID.
     * @param  {string} [siteId] Site ID. If not defined, current site.
     * @return {Promisee<boolean>} Promise resolved with true if plugin is enabled, rejected or resolved with false otherwise.
     */
    isPluginEnabledForCourse(courseId: number, siteId?: string): Promise<boolean> {
        if (!courseId) {
            return Promise.reject(null);
        }

        return this.coursesProvider.getUserCourse(courseId, true, siteId).then((course) => {
            return !(course && typeof course.showgrades != 'undefined' && course.showgrades == 0);
        });
    }

    /**
     * Returns whether or not WS Grade Items is avalaible.
     *
     * @param  {string} [siteId] Site ID. If not defined, current site.
     * @return {Promise<boolean>}         True if ws is avalaible, false otherwise.
     * @since  Moodle 3.2
     */
    isGradeItemsAvalaible(siteId?: string): Promise<boolean> {
        return this.sitesProvider.getSite(siteId).then((site) => {
            return site.wsAvailable('gradereport_user_get_grade_items');
        });
    }

    /**
     * Log Course grades view in Moodle.
     *
     * @param  {number}  courseId Course ID.
     * @param  {number}  userId   User ID.
     * @return {Promise<any>}     Promise resolved when done.
     */
    logCourseGradesView(courseId: number, userId: number): Promise<any> {
        userId = userId || this.sitesProvider.getCurrentSiteUserId();

        return this.sitesProvider.getCurrentSite().write('gradereport_user_view_grade_report', {
            courseid: courseId,
            userid: userId
        });
    }

    /**
     * Log Courses grades view in Moodle.
     *
     * @param  {number}  [courseId] Course ID. If not defined, site Home ID.
     * @return {Promise<any>}     Promise resolved when done.
     */
    logCoursesGradesView(courseId?: number): Promise<any> {
        if (!courseId) {
            courseId = this.sitesProvider.getCurrentSiteHomeId();
        }

        return this.sitesProvider.getCurrentSite().write('gradereport_overview_view_grade_report', {
            courseid: courseId
        });
    }
}