import json
import os
import random
import re
import shutil
import tempfile
from datetime import datetime

import pandas
from cognitiveatlas.api import get_concept, get_task
from django.db.models import Min
from expfactory.experiment import get_experiments
from expfactory.survey import export_questions
from expfactory.utils import copy_directory
from expfactory.vm import custom_battery_download
from git import Repo
from numpy.random import choice

from expdj.apps.experiments.models import (CognitiveAtlasConcept,
                                           CognitiveAtlasTask, Experiment,
                                           ExperimentBooleanVariable,
                                           ExperimentNumericVariable,
                                           ExperimentStringVariable,
                                           ExperimentTemplate,
                                           ExperimentVariable)
from expdj.apps.turk.models import Result
from expdj.settings import BASE_DIR, MEDIA_ROOT, STATIC_ROOT, EXP_REPO

media_dir = os.path.join(BASE_DIR, MEDIA_ROOT)

# EXPERIMENT FACTORY PYTHON FUNCTIONS ####################################


def get_experiment_selection(repo_type="experiments"):
    # tmpdir = custom_battery_download(repos=[repo_type])
    tmpdir = os.path.join(EXP_REPO, 'expfactory-{}'.format(repo_type))
    experiments = get_experiments(
        tmpdir,
        load=True,
        warning=False,
        repo_type=repo_type)
    experiments = [x[0] for x in experiments]
    # shutil.rmtree(tmpdir)
    return experiments


def get_experiment_type(experiment):
    '''get_experiment_type returns the installation folder (eg, games, surveys,
        experiments) based on the template specified in the config.json
    :param experiment: the ExperimentTemplate object
    '''
    if experiment.template in ["jspsych"]:
        return "experiments"
    elif experiment.template in ["survey"]:
        return "surveys"
    elif experiment.template in ["phaser"]:
        return "games"


def parse_experiment_variable(variable):
    experiment_variable = None
    if isinstance(variable, dict):
        try:
            description = variable["description"] if "description" in variable.keys(
            ) else None
            if "name" in variable.keys():
                name = variable["name"]
                if "datatype" in variable.keys():
                    if variable["datatype"].lower() == "numeric":
                        variable_min = variable["range"][0] if "range" in variable.keys(
                        ) else None
                        variable_max = variable["range"][1] if "range" in variable.keys(
                        ) else None
                        experiment_variable, _ = ExperimentNumericVariable.objects.update_or_create(
                            name=name, description=description, variable_min=variable_min, variable_max=variable_max)
                    elif variable["datatype"].lower() == "string":
                        experiment_variable, _ = ExperimentStringVariable.objects.update_or_create(
                            name=name, description=description, variable_options=variable_options)
                    elif variable["datatype"].lower() == "boolean":
                        experiment_variable, _ = ExperimentBooleanVariable.objects.update_or_create(
                            name=name, description=description)
                    experiment_variable.save()
        except BaseException:
            pass
    return experiment_variable


def install_experiments(experiment_tags=None, repo_type="experiments"):

    # We will return list of experiments that did not install successfully
    errored_experiments = []

    # tmpdir = custom_battery_download(repos=[repo_type, "battery"])
    tmpdir = os.path.join(EXP_REPO, 'expfactory-{}'.format(repo_type))

    # could catch non existant repos with git.exc.InvalidGitRepositoryError
    # repo = Repo("%s/%s" % (tmpdir, repo_type))
    repo = Repo(tmpdir)

    # The git commit is saved with the experiment as the "version"
    commit = repo.commit('master').__str__()

    experiments = get_experiments(tmpdir, load=True, warning=False)
    if experiment_tags is not None:
        experiments = [e for e in experiments if e[0]
                       ["exp_id"] in experiment_tags]

    for experiment in experiments:
        try:
            performance_variable = None
            rejection_variable = None
            if "experiment_variables" in experiment[0]:
                if isinstance(experiment[0]["experiment_variables"], list):
                    for var in experiment[0]["experiment_variables"]:
                        if var["type"].lower().strip() == "bonus":
                            performance_variable = parse_experiment_variable(
                                var)
                        elif var["type"].lower().strip() == "credit":
                            rejection_variable = parse_experiment_variable(var)
                        else:
                            parse_experiment_variable(var)  # adds to database
            if isinstance(experiment[0]["reference"], list):
                reference = experiment[0]["reference"][0]
            else:
                reference = experiment[0]["reference"]
            cognitive_atlas_task = get_cognitiveatlas_task(
                experiment[0]["cognitive_atlas_task_id"])

            new_experiment, _ = ExperimentTemplate.objects.update_or_create(
                exp_id=experiment[0]["exp_id"],
                defaults={
                    "name": experiment[0]["name"],
                    "cognitive_atlas_task": cognitive_atlas_task,
                    "publish": bool(experiment[0]["publish"]),
                    "time": experiment[0]["time"],
                    "reference": reference,
                    "version": commit,
                    "template": experiment[0]["template"],
                    "performance_variable": performance_variable,
                    "rejection_variable": rejection_variable
                }
            )

            new_experiment.save()
            experiment_folder = "%s/%s" % (tmpdir, experiment[0]["exp_id"])
            output_folder = "%s/%s/%s" % (media_dir,
                                          repo_type, experiment[0]["exp_id"])
            if os.path.exists(output_folder):
                shutil.rmtree(output_folder)
            copy_directory(experiment_folder, output_folder)
        except BaseException:
            errored_experiments.append(experiment[0]["exp_id"])

    # shutil.rmtree(tmpdir)
    return errored_experiments

# EXPERIMENTS AND BATTERIES ##############################################


def make_experiment_lookup(tags, battery=None):
    '''Generate lookup based on exp_id'''
    experiment_lookup = dict()
    for tag in tags:
        experiment = None
        try:
            if battery is not None:
                # First try retrieving from battery
                experiment = battery.experiments.filter(
                    template__exp_id=tag)[0]
                if isinstance(experiment, Experiment):
                    tmp = {"include_bonus": experiment.include_bonus,
                           "include_catch": experiment.include_catch,
                           "experiment": experiment.template}
                else:
                    experiment = None
            if experiment is None:
                experiment = ExperimentTemplate.objects.filter(exp_id=tag)[0]
                tmp = {"include_bonus": "Unknown",
                       "include_catch": "Unknown",
                       "experiment": experiment}
            experiment_lookup[tag] = tmp
        except BaseException:
            pass
    return experiment_lookup


def get_battery_results(battery, exp_id=None, clean=False):
    '''get_battery_results filters down to a battery, and optionally, an experiment of interest
    :param battery: expdj.models.Battery
    :param expid: an ExperimentTemplate.tag variable, eg "test_task"