import { Meteor } from 'meteor/meteor';
import { Mongo } from 'meteor/mongo';
import { check } from 'meteor/check';
import {Sites} from "./sites";
import { Roles } from 'meteor/alanning:roles';
import {parse} from 'csv-parse';

export const Students = new Mongo.Collection('students');

if (Meteor.isServer) {
	Students.createIndex({id: 1}, {name: "External ID", unique: true});

	// This code only runs on the server
	Meteor.publish('students', function(siteId) {
		if(siteId) check(siteId, String);
		return siteId ? Students.find({siteId}) : Students.find({});
	});

	Meteor.methods({
		'students.add'(id, firstName, lastName, email, siteId, grade) {
			if(Roles.userIsInRole(Meteor.userId(), "admin", {anyScope:true})) {
				Students.insert({id, firstName, lastName, email, siteId, grade});
			}
		},
		'students.update'(_id, id, firstName, lastName, email, siteId, grade) {
			if(Roles.userIsInRole(Meteor.userId(), "admin", {anyScope:true})) {
				Students.update({_id}, {$set: {id, firstName, lastName, email, siteId, grade}});
			}
		},
		'students.remove'(_id) {
			if(Roles.userIsInRole(Meteor.userId(), "admin", {anyScope:true})) {
				//TODO: Need to first verify there are no checked out assets to the staff member.
			}
		},
		'students.getPossibleGrades'() {
			return Students.rawCollection().distinct('grade', {});
		},
		/**
		 * Sets a first name alias that can be overridden by the one that is imported.
		 * @param _id The student's database ID.
		 * @param alias The alias to set for the student.
		 */
		'students.setAlias'(_id, alias) {
			if(Roles.userIsInRole(Meteor.userId(), "admin", {anyScope:true})) {
				check(_id, String);
				check(alias, String);

				Students.update({_id}, !alias || !alias.length() ? {$unset: {alias: true}} : {$set: {alias}});
			}
		},
		/**
		 * Assumes that the ID field is a unique ID that never changes for a student.
		 * This must be true in order for duplicate students to be avoided.
		 * Will automatically update a student's data, including the site he/she is associated with.
		 *
		 * Expects the CSV string to contain comma delimited data in the form:
		 * email, student ID, first name, last name, grade, first name alias, last name alias
		 *
		 * The query in Aeries is: `LIST STU ID SEM FN LN NG FNA`.
		 * A more complete Aeries query: `LIST STU STU.ID STU.SEM STU.FN STU.LN STU.NG BY STU.NG STU.SEM IF STU.NG >= 7 AND NG <= 12 AND STU.NS = 5`
		 * Note that FNA (First Name Alias) is optional.
		 * Note that you might want to include a school ID in the IF if you have multiple schools in the district.
		 * The query in SQL is: `SELECT [STU].[ID] AS [Student ID], [STU].[SEM] AS [StuEmail], STU.FN AS [First Name], STU.LN AS [Last Name], [STU].[GR] AS [Grade], [STU].[FNA] AS [First Name Alias], [STU].[LNA] AS [Last Name Alias] FROM (SELECT [STU].* FROM STU WHERE [STU].DEL = 0) STU WHERE ( [STU].SC = 5) ORDER BY [STU].[LN], [STU].[FN];`.
		 * Run the query in Aeries as a `Report`, select TXT, and upload here.
		 *
		 * Aeries adds a header per 'page' of data (I think 35 entries per page).
		 * Example:
		 * Anderson Valley Jr/Sr High School,6/11/2022
		 * 2021-2022,Page 1
		 * Student ID, Email, First Name,Last Name,Grade,(opt) First Name Alias
		 * @type: Currently only supports 'CSV' or 'Aeries Text Report'
		 */
		'students.loadCsv'(csv, type, siteId) {
			if(Roles.userIsInRole(Meteor.userId(), "admin", {anyScope:true})) {
				check(csv, String);
				check(siteId, String);

				let site = Sites.findOne({_id: siteId});

				if(site) {
					let cleanCsv;
					let lines = csv.split(/\r?\n/);
					let pageHeader = type === 'Aeries Text Report' ? lines[0] : null;  // Skip the repeating header lines for an Aeries text report.
					let skip = type === 'CSV' ? 1 : 0;  // Skip the first line of a CSV file (headers).

					// Remove headers from the CSV.
					for(const line of lines) {
						if (skip > 0) skip--;
						else if (pageHeader && line === pageHeader) {
							skip = 2;
						} else {
							if(!cleanCsv) cleanCsv = "";
							else cleanCsv += '\r\n';
							cleanCsv += line;
						}
					}

					const bound = Meteor.bindEnvironment((callback) => {callback();});

					parse(cleanCsv, {}, function(err, records) {
						bound(() => {
							if(err) console.error(err);
							else {
								let foundIds = new Set();
								let duplicates = [];
								console.log("Found " + records.length + " records.");

								for(const values of records) {
									let id = values[0];
									let email = values[1];
									let firstName = values[2];
									let lastName = values[3];
									let grade = parseInt(values[4], 10);
									let firstNameAlias = "";
									let active = true;

									if(values.length > 5) firstNameAlias = values[5];

									let student = {siteId, email, id, firstName, lastName, grade, firstNameAlias, active};

									// Track the student ID's and record duplicates.  This is used to ensure our counts are accurate later.
									if(foundIds.has(student.id)) {
										duplicates.push(student.id);
									}
									else {
										foundIds.add(student.id);
									}

									try {
										Students.upsert({id: student.id}, {$set: student});
									}
									catch(err) {
										console.error(err);
									}
								}

								console.log(duplicates.length + " records were duplicates:");
								console.log(duplicates);
							}
						});
					})
				}
				else {
					console.log("Failed to find the site with the ID: " + siteId);
				}
			}
		}
	});

}