import hashlib
import os

import nbformat
import nbconvert
from nbgrader import utils
from traitlets.config import Config


def find_path(codename):
    for root, dirs, files in os.walk(".", topdown=False):
        for dir_ in dirs:
            if dir_.lower().startswith(codename.lower()):
                return os.path.join(root, dir_)

    raise RuntimeError("Learning Unit directory not found")


def find_exercise_nb(codename):
    path = find_path(codename)
    return os.path.join(path, "Exercise notebook.ipynb")


def calculate_checksum(nb):
    m = hashlib.sha256()
    for cell in nb.cells:
        if utils.is_grade(cell):
            grade_id = cell.metadata.nbgrader["grade_id"]
            checksum = utils.compute_checksum(cell)
            m.update(grade_id.encode("utf-8"))
            m.update(checksum.encode("utf-8"))

    return m.hexdigest()


def grade(nb):
    total_score = 0
    max_total_score = 0
    for cell in nb.cells:
        if utils.is_grade(cell):
            score, max_score = utils.determine_grade(cell)
            total_score += score
            max_total_score += max_score

    return total_score, max_total_score


def is_valid(nb, checksum):
    return calculate_checksum(nb) == checksum


def execute(notebook, timeout=None, allow_errors=True):
    c = Config()
    c.NotebookExporter.preprocessors = [
        "nbconvert.preprocessors.ClearOutputPreprocessor",
        "nbconvert.preprocessors.ExecutePreprocessor",
    ]
    c.ExecutePreprocessor.allow_errors = allow_errors
    if timeout:
        c.ExecutePreprocessor.timeout = timeout

    exporter = nbconvert.NotebookExporter(config=c)
    notebook, _ = exporter.from_notebook_node(notebook)

    return nbformat.reads(notebook, as_version=nbformat.NO_CONVERT)


def clear(notebook, allow_hidden_tests=False):
    c = Config()
    c.NotebookExporter.preprocessors = [
        "nbconvert.preprocessors.ClearOutputPreprocessor",
        "nbgrader.preprocessors.ClearSolutions",
        "nbgrader.preprocessors.LockCells",
    ]
    if not allow_hidden_tests:
        c.NotebookExporter.preprocessors.append(
            "ldsagrader.preprocessors.ForbidHiddenTests")

    exporter = nbconvert.NotebookExporter(config=c)
    notebook, _ = exporter.from_notebook_node(notebook)

    return nbformat.reads(notebook, as_version=nbformat.NO_CONVERT)
