"use strict";

const async = require("async");

const hooks = require("./hooks");

const moduleManager = require("../../index");

const db = require("../db");
const cache = require("../cache");
const utils = require("../utils");
// const logger = require("../logger");
const songs = require("../songs");

const reportableIssues = [
    {
        name: "Video",
        reasons: [
            "Doesn't exist",
            "It's private",
            "It's not available in my country",
        ],
    },
    {
        name: "Title",
        reasons: ["Incorrect", "Inappropriate"],
    },
    {
        name: "Duration",
        reasons: [
            "Skips too soon",
            "Skips too late",
            "Starts too soon",
            "Skips too late",
        ],
    },
    {
        name: "Artists",
        reasons: ["Incorrect", "Inappropriate"],
    },
    {
        name: "Thumbnail",
        reasons: ["Incorrect", "Inappropriate", "Doesn't exist"],
    },
];

cache.runJob("SUB", {
    channel: "report.resolve",
    cb: (reportId) => {
        utils.runJob("EMIT_TO_ROOM", {
            room: "admin.reports",
            args: ["event:admin.report.resolved", reportId],
        });
    },
});

cache.runJob("SUB", {
    channel: "report.create",
    cb: (report) => {
        utils.runJob("EMIT_TO_ROOM", {
            room: "admin.reports",
            args: ["event:admin.report.created", report],
        });
    },
});

module.exports = {
    /**
     * Gets all reports
     *
     * @param {Object} session - the session object automatically added by socket.io
     * @param {Function} cb - gets called with the result
     */
    index: hooks.adminRequired(async (session, cb) => {
        const reportModel = await db.runJob("GET_MODEL", {
            modelName: "report",
        });
        async.waterfall(
            [
                (next) => {
                    reportModel
                        .find({ resolved: false })
                        .sort({ released: "desc" })
                        .exec(next);
                },
            ],
            async (err, reports) => {
                if (err) {
                    err = await utils.runJob("GET_ERROR", { error: err });
                    console.log(
                        "ERROR",
                        "REPORTS_INDEX",
                        `Indexing reports failed. "${err}"`
                    );
                    return cb({ status: "failure", message: err });
                }
                console.log(
                    "SUCCESS",
                    "REPORTS_INDEX",
                    "Indexing reports successful."
                );
                cb({ status: "success", data: reports });
            }
        );
    }),

    /**
     * Gets a specific report
     *
     * @param {Object} session - the session object automatically added by socket.io
     * @param {String} reportId - the id of the report to return
     * @param {Function} cb - gets called with the result
     */
    findOne: hooks.adminRequired(async (session, reportId, cb) => {
        const reportModel = await db.runJob("GET_MODEL", {
            modelName: "report",
        });
        async.waterfall(
            [
                (next) => {
                    reportModel.findOne({ _id: reportId }).exec(next);
                },
            ],
            async (err, report) => {
                if (err) {
                    err = await utils.runJob("GET_ERROR", { error: err });
                    console.log(
                        "ERROR",
                        "REPORTS_FIND_ONE",
                        `Finding report "${reportId}" failed. "${err}"`
                    );
                    return cb({ status: "failure", message: err });
                }
                console.log(
                    "SUCCESS",
                    "REPORTS_FIND_ONE",
                    `Finding report "${reportId}" successful.`
                );
                cb({ status: "success", data: report });
            }
        );
    }),

    /**
     * Gets all reports for a songId (_id)
     *
     * @param {Object} session - the session object automatically added by socket.io
     * @param {String} songId - the id of the song to index reports for
     * @param {Function} cb - gets called with the result
     */
    getReportsForSong: hooks.adminRequired(async (session, songId, cb) => {
        const reportModel = await db.runJob("GET_MODEL", {
            modelName: "report",
        });
        async.waterfall(
            [
                (next) => {
                    reportModel
                        .find({ song: { _id: songId }, resolved: false })
                        .sort({ released: "desc" })
                        .exec(next);
                },

                (reports, next) => {
                    let data = [];
                    for (let i = 0; i < reports.length; i++) {
                        data.push(reports[i]._id);
                    }
                    next(null, data);
                },
            ],
            async (err, data) => {
                if (err) {
                    err = await utils.runJob("GET_ERROR", { error: err });
                    console.log(
                        "ERROR",
                        "GET_REPORTS_FOR_SONG",
                        `Indexing reports for song "${songId}" failed. "${err}"`
                    );
                    return cb({ status: "failure", message: err });
                } else {
                    console.log(
                        "SUCCESS",
                        "GET_REPORTS_FOR_SONG",
                        `Indexing reports for song "${songId}" successful.`
                    );
                    return cb({ status: "success", data });
                }
            }
        );
    }),

    /**
     * Resolves a report
     *
     * @param {Object} session - the session object automatically added by socket.io
     * @param {String} reportId - the id of the report that is getting resolved
     * @param {Function} cb - gets called with the result
     */
    resolve: hooks.adminRequired(async (session, reportId, cb) => {
        const reportModel = await db.runJob("GET_MODEL", {
            modelName: "report",
        });
        async.waterfall(
            [
                (next) => {
                    reportModel.findOne({ _id: reportId }).exec(next);
                },

                (report, next) => {
                    if (!report) return next("Report not found.");
                    report.resolved = true;
                    report.save((err) => {
                        if (err) next(err.message);
                        else next();
                    });
                },
            ],
            async (err) => {
                if (err) {
                    err = await utils.runJob("GET_ERROR", { error: err });
                    console.log(
                        "ERROR",
                        "REPORTS_RESOLVE",
                        `Resolving report "${reportId}" failed by user "${session.userId}". "${err}"`
                    );
                    return cb({ status: "failure", message: err });
                } else {
                    cache.runJob("PUB", {
                        channel: "report.resolve",
                        value: reportId,
                    });
                    console.log(
                        "SUCCESS",
                        "REPORTS_RESOLVE",
                        `User "${session.userId}" resolved report "${reportId}".`
                    );
                    cb({
                        status: "success",
                        message: "Successfully resolved Report",
                    });
                }
            }
        );
    }),

    /**
     * Creates a new report
     *
     * @param {Object} session - the session object automatically added by socket.io
     * @param {Object} data - the object of the report data
     * @param {Function} cb - gets called with the result
     */
    create: hooks.loginRequired(async (session, data, cb) => {
        const reportModel = await db.runJob("GET_MODEL", {
            modelName: "report",
        });
        const songModel = await db.runJob("GET_MODEL", { modelName: "song" });
        async.waterfall(
            [
                (next) => {
                    songModel.findOne({ songId: data.songId }).exec(next);
                },

                (song, next) => {
                    if (!song) return next("Song not found.");
                    songs
                        .runJob("GET_SONG", { id: song._id })
                        .then((response) => next(null, response.song))
                        .catch(next);
                },

                (song, next) => {
                    if (!song) return next("Song not found.");

                    delete data.songId;
                    data.song = {
                        _id: song._id,
                        songId: song.songId,
                    };

                    for (let z = 0; z < data.issues.length; z++) {
                        if (
                            reportableIssues.filter((issue) => {
                                return issue.name == data.issues[z].name;
                            }).length > 0
                        ) {
                            for (let r = 0; r < reportableIssues.length; r++) {
                                if (
                                    reportableIssues[r].reasons.every(
                                        (reason) =>
                                            data.issues[z].reasons.indexOf(
                                                reason
                                            ) < -1
                                    )
                                ) {
                                    return cb({
                                        status: "failure",
                                        message: "Invalid data",
                                    });
                                }
                            }
                        } else
                            return cb({
                                status: "failure",
                                message: "Invalid data",
                            });
                    }

                    next();
                },

                (next) => {
                    let issues = [];

                    for (let r = 0; r < data.issues.length; r++) {
                        if (!data.issues[r].reasons.length <= 0)
                            issues.push(data.issues[r]);
                    }

                    data.issues = issues;

                    next();
                },

                (next) => {
                    data.createdBy = session.userId;
                    data.createdAt = Date.now();
                    reportModel.create(data, next);
                },
            ],
            async (err, report) => {
                if (err) {
                    err = await utils.runJob("GET_ERROR", { error: err });
                    console.log(
                        "ERROR",
                        "REPORTS_CREATE",
                        `Creating report for "${data.song._id}" failed by user "${session.userId}". "${err}"`
                    );
                    return cb({ status: "failure", message: err });
                } else {
                    cache.runJob("PUB", {
                        channel: "report.create",
                        value: report,
                    });
                    console.log(
                        "SUCCESS",
                        "REPORTS_CREATE",
                        `User "${session.userId}" created report for "${data.songId}".`
                    );
                    return cb({
                        status: "success",
                        message: "Successfully created report",
                    });
                }
            }
        );
    }),
};