import secrets
from datetime import datetime
from uuid import uuid4
from flask import flash, jsonify
import secrets
import os
from json import dump, loads

from common.security import encrypt

class Test:
    
    def __init__(self, _id=None, start_date=None, expiry_date=None, time_limit=None, creator=None, dataset=None):
        self._id = _id
        self.start_date = start_date
        self.expiry_date = expiry_date
        self.time_limit = None if time_limit == 'none' or time_limit == '' or time_limit == None else int(time_limit)
        self.creator = creator
        self.dataset = dataset
    
    def create(self):
        from main import app, db
        test = {
            '_id': self._id,
            'date_created': datetime.today(),
            'start_date': self.start_date,
            'expiry_date': self.expiry_date,
            'time_limit': self.time_limit,
            'creator': encrypt(self.creator),
            'test_code': secrets.token_hex(6).upper(),
            'dataset': self.dataset
        }
        if db.tests.insert_one(test):
            dataset_file_path = os.path.join(app.config["DATA_FILE_DIRECTORY"],self.dataset)
            with open(dataset_file_path, 'r') as dataset_file:
                data = loads(dataset_file.read())
            data['meta']['tests'].append(self._id)
            with open(dataset_file_path, 'w') as dataset_file:
                dump(data, dataset_file, indent=2)
            flash(f'Created a new exam with Exam Code <strong>{self.render_test_code(test["test_code"])}</strong>.', 'success')
            return jsonify({'success': test}), 200
        return jsonify({'error': f'Could not create exam. An error occurred.'}), 400

    def add_time_adjustment(self, time_adjustment):
        from main import db
        user_code = secrets.token_hex(3).upper()
        adjustment = {
            user_code: time_adjustment
        }
        if db.tests.find_one_and_update({'_id': self._id}, {'$set': {'time_adjustments': adjustment}},upsert=False):
            flash(f'Time adjustment for {time_adjustment} minutes has been added. This can be enabled using the user code {user_code}.')
            return jsonify({'success': adjustment})
        return jsonify({'error': 'Failed to add the time adjustment. An error occurred.'}), 400

    def remove_time_adjustment(self, user_code):
        from main import db
        if db.tests.find_one_and_update({'_id': self._id}, {'$unset': {f'time_adjustments.{user_code}': {}}}):
            message = 'Time adjustment has been deleted.'
            flash(message, 'success')
            return jsonify({'success': message})
        return jsonify({'error': 'Failed to delete the time adjustment. An error occurred.'}), 400

    def render_test_code(self, test_code):
        return '—'.join([test_code[:4], test_code[4:8], test_code[8:]])
    
    def parse_test_code(self, test_code):
        return test_code.replace('—', '')
    
    def delete(self):
        from main import app, db
        test = db.tests.find_one({'_id': self._id})
        if 'entries' in test:
            if test['entries']:
                return jsonify({'error': 'Cannot delete an exam that has entries submitted to it.'}), 400
        if self.dataset is None:
            self.dataset = db.tests.find_one({'_id': self._id})['dataset']
        if db.tests.delete_one({'_id': self._id}):
            dataset_file_path = os.path.join(app.config["DATA_FILE_DIRECTORY"],self.dataset)
            with open(dataset_file_path, 'r') as dataset_file:
                data = loads(dataset_file.read())
            data['meta']['tests'].remove(self._id)
            with open(dataset_file_path, 'w') as dataset_file:
                dump(data, dataset_file, indent=2)
            message = 'Deleted exam.'
            flash(message, 'alert')
            return jsonify({'success': message}), 200
        return jsonify({'error': f'Could not create exam. An error occurred.'}), 400

    def update(self):
        from main import db
        test = {}
        updated = []
        if not self.start_date == '' and self.start_date is not None:
            test['start_date'] = self.start_date
            updated.append('start date')
        if not self.expiry_date == '' and self.expiry_date is not None:
            test['expiry_date'] = self.expiry_date
            updated.append('expiry date')
        if not self.time_limit == '' and self.time_limit is not None:
            test['time_limit'] = int(self.time_limit)
            updated.append('time limit')
        output = ''
        if len(updated) == 0:
            flash(f'There were no changes requested for your account.', 'alert'), 200
            return jsonify({'success': 'There were no changes requested for your account.'}), 200
        elif len(updated) == 1:
            output = updated[0]
        elif len(updated) == 2:
            output = ' and '.join(updated)
        elif len(updated) > 2:
            output = updated[0]
            for index in range(1,len(updated)):
                if index < len(updated) - 2:
                    output = ', '.join([output, updated[index]])
                elif index == len(updated) - 2:
                    output = ', and '.join([output, updated[index]])
                else:
                    output = ''.join([output, updated[index]])
        db.tests.find_one_and_update({'_id': self._id}, {'$set': test})
        _output = f'The {output} of the test {"has" if len(updated) == 1 else "have"} been updated.'
        flash(_output)
        return jsonify({'success': _output}), 200