from django.utils.decorators import method_decorator
from django.views.decorators.csrf import csrf_exempt
from django.shortcuts import render, redirect
from common.models import *
from django.db.models import Q
from django.contrib.auth.decorators import login_required
from .forms import RequestForm, SequenceForm, AlbumForm, PlanForm
from .validators import check_plan_validity, check_album_validity, check_sequence_validity, check_request_validity
from .RequestSerializer import RequestSerializer
import scheduler

""" logger """
from django.conf import settings
import utils.Logger as l
log = l.setupLogger("routine_manager-views", "routine_manager-views")

""" XML Export / Import utils """
from wsgiref.util import FileWrapper
from django.utils.encoding import smart_str
from django.http import HttpResponse
import mimetypes
import os
from pprint import pprint

SAVED_REQUESTS_FOLDER = "misc/saved_requests/"


@login_required
def requests_list(request, status=0, message=""):
    """
        Retrieves and display the routines list (routine manager main page)
    """

    if settings.DEBUG:
        log.info("From requests_list")

    if status == "-1":
        error = True
    elif status == "1":
        success = True

    # TODO: uncomment for alert filter
#     requests_objs = Request.objects.filter(pyros_user=request.user).filter(is_alert=False).order_by("-updated")

    requests_objs = Request.objects.filter(pyros_user=request.user).order_by("-updated")
    print("REQ/views: Les requetes sont:")

    requests = []

    for req in requests_objs:
        #print("- REQ/views: requete", req)
        sequences = req.sequences
        nb_executed = sequences.filter(status=Sequence.EXECUTED).count
        nb_cancelled = sequences.filter(Q(status=Sequence.INVALID) | Q(status=Sequence.CANCELLED)
                                        | Q(status=Sequence.UNPLANNABLE)).count()
        requests.append({'req': req, 'nb_seq': sequences.count(), 'nb_executed': nb_executed, 'nb_cancelled': nb_cancelled})

    print("REQ/views: ICI:")
    # opens template src/routine_manager/templates/routine_manager/requests_list.html with all local variables
    return render(request, "routine_manager/requests_list.html", locals())

@login_required
def action_request(request, req_id, action, status=0, message=""):
    """
        Apply actions (view, edit, delete) on a given request
    """

    req = Request.objects.get(id=req_id)
    depth_level = 1

    if settings.DEBUG:
        log.info("From action_request")

    if status == "-1":
        error = True
    elif status == "1":
        success = True

    if req.submitted == True and action == "edit":
        error = True
        message = "You can't edit a submitted request"
        action = "view"

    if check_request_validity(req) == True:
        return redirect(unsubmit_request, req_id)
    if action == "edit":
        form = RequestForm(instance=req)
        edit = True
        return render(request, "routine_manager/view_request.html", locals())
    elif action == "view":
        form = RequestForm(instance=req, readonly=True)
        edit = False
        return render(request, "routine_manager/view_request.html", locals())
    elif action == "delete":
        req.delete()
        return redirect(requests_list, status='1', message="Successfully deleted the request")

    return redirect(requests_list)

@login_required
def request_validate(request, req_id):
    """
        Called when the request form was validated.
        Possible actions : Cancel, Save, Save and add sequence, Delete
    """

    action = request.POST.get("action")
    req = Request.objects.get(id=req_id)
    form = RequestForm(instance=req, data=request.POST)

    if (settings.DEBUG):
        log.info("From request_validate")

    if action == "cancel":
        if req.name == "New request":
            req.delete()
        return redirect(requests_list, status=1, message="Cancelled request modification")
    elif action == "delete":
        return redirect(action_request, req_id, "delete")
    elif form.is_valid():
        req = form.save()
        if action == "save":
            return redirect(action_request, req_id=req_id, action="edit", status=1, message="Request saved")
        if action == "save_and_add":
            return redirect(create_sequence, req_id=req_id)
    else:
        error = True
        message = "Please check your field's validity"
        depth_level = 1
        edit = True
        return render(request, "routine_manager/view_request.html", locals())


@login_required
def action_sequence(request, seq_id, action, status=0, message=""):
    """
        Apply actions (view, edit, delete) on a given sequence
    """

    seq_id = int(seq_id)
    seq = Sequence.objects.get(id=seq_id)
    req = seq.request
    req_id = req.id
    depth_level = 2

    if (settings.DEBUG):
        log.info("From action_sequence")

    if status == "-1":
        error = True
    elif status == "1":
        success = True

    if seq.status != Sequence.INCOMPLETE and seq.status != Sequence.COMPLETE and action == "edit":
        error = True
        message = "You can't edit a submitted request"
        action = "view"

    if check_sequence_validity(seq) == True:
        return redirect(unsubmit_request, req_id)
    if action == "edit":
        form = SequenceForm(instance=seq)
        edit = True
        return render(request, "routine_manager/view_sequence.html", locals())
    elif action == "view":
        form = SequenceForm(instance=seq, readonly=True)
        edit = False
        return render(request, "routine_manager/view_sequence.html", locals())
    elif action == "delete":
        seq.delete()
        if check_request_validity(req) == True:
            return redirect(unsubmit_request, req_id)
        return redirect(action_request, req_id=seq.request.id, action="edit", status='1', message="Successfully deleted the sequence")

    return redirect(requests_list)

@login_required
def sequence_validate(request, seq_id):
    """
        Called when the sequence form was validated.
        Possible actions : Cancel, Save, Save and add album, Delete
    """

    if (settings.DEBUG):
        log.info("From sequence_validate")

    seq_id = int(seq_id)
    action = request.POST.get("action")
    seq = Sequence.objects.get(id=seq_id)
    form = SequenceForm(instance=seq, data=request.POST)

    if action == "cancel":
        if seq.name == "New sequence":
            seq.delete()
        return redirect(action_request, req_id=seq.request.id, action="edit", status='1', message="Cancelled sequence modification")
    elif action == "delete":
        return redirect(action_sequence, seq_id, "delete")
    elif form.is_valid():
        seq = form.save()
        if action == "save":
            return redirect(action_sequence, seq_id=seq_id, action="edit", status=1, message="Sequence saved")
        if action == "save_and_add":
            return redirect(create_album, seq_id=seq_id)
    else:
        error = True
        message = "Please check your field's validity"
        depth_level = 2
        edit = True
        req = seq.request
        req_id = req.id
        return render(request, "routine_manager/view_sequence.html", locals())

@login_required
def action_album(request, alb_id, action, status=0, message=""):
    """
        Apply actions (view, edit, delete) on a given album
    """

    alb_id = int(alb_id)
    alb = Album.objects.get(id=alb_id)
    req = alb.sequence.request
    req_id = req.id
    seq_id = alb.sequence.id
    depth_level = 3

    if (settings.DEBUG):
        log.info("From action_album")

    if status == "-1":
        error = True
    elif status == "1":
        success = True

    if alb.sequence.status != Sequence.INCOMPLETE and alb.sequence.status != Sequence.COMPLETE and action == "edit":
        error = True
        message = "You can't edit a submitted request"
        action = "view"

    if check_album_validity(alb) == True:
        return redirect(unsubmit_request, req_id)
    if action == "edit":
        form = AlbumForm(instance=alb)
        edit = True
        return render(request, "routine_manager/view_album.html", locals())
    elif action == "view":
        form = AlbumForm(instance=alb, readonly=True)
        edit = False
        return render(request, "routine_manager/view_album.html", locals())
    elif action == "delete":
        alb.delete()
        return redirect(action_sequence, seq_id=alb.sequence.id, action="edit", status='1', message="Successfully deleted the album")

    return redirect(requests_list)

@login_required
def album_validate(request, alb_id):
    """
        Called when the album form was validated.
        Possible actions : Cancel, Save, Save and add plan, Delete
    """

    alb_id = int(alb_id)
    action = request.POST.get("action")
    alb = Album.objects.get(id=alb_id)
    form = AlbumForm(instance=alb, data=request.POST)

    if (settings.DEBUG):
        log.info("From album_validate")


    if action == "cancel":
        if alb.name == "New album":
            alb.delete()
        return redirect(action_sequence, seq_id=alb.sequence.id, action="edit", status='1', message="Cancelled album modification")
    elif action == "delete":
        return redirect(action_album, alb_id, "delete")
    elif form.is_valid():
        alb = form.save()
        if action == "save":
            return redirect(action_album, alb_id=alb_id, action="edit", status=1, message="Album saved")
        if action == "save_and_add":
            return redirect(create_plan, alb_id=alb_id)
    else:
        error = True
        message = "Please check your field's validity"
        depth_level = 3
        edit = True
        req = alb.sequence.request
        req_id = req.id
        seq_id = alb.sequence.id
        action = "edit"
        return render(request, "routine_manager/view_album.html", locals())

@login_required
def action_plan(request, plan_id, action, status=0, message=""):
    """
        Apply actions (view, edit, delete) on a given plan
    """

    plan_id = int(plan_id)
    plan = Plan.objects.get(id=plan_id)
    req = plan.album.sequence.request
    req_id = req.id
    seq_id = plan.album.sequence.id
    alb_id = plan.album.id
    depth_level = 4

    if (settings.DEBUG):
        log.info("From action_plan")

    if status == "-1":
        error = True
    elif status == "1":
        success = True

    if plan.album.sequence.status != Sequence.INCOMPLETE and plan.album.sequence.status != Sequence.COMPLETE and action == "edit":
        error = True
        message = "You can't edit a submitted request"
        action = "view"

    if check_plan_validity(plan) == True:
        return redirect(unsubmit_request, req_id)
    if action == "edit":
        form = PlanForm(instance=plan)
        edit = True
        return render(request, "routine_manager/view_plan.html", locals())
    elif action == "view":
        form = PlanForm(instance=plan, readonly=True)
        edit = False
        return render(request, "routine_manager/view_plan.html", locals())
    elif action == "delete":
        plan.delete()
        return redirect(action_album, alb_id=plan.album.id, action="edit", status='1', message="Successfully deleted the plan")

    return redirect(requests_list)

@login_required
def plan_validate(request, plan_id):
    """
        Called when the plan form was validated.
        Possible actions : Cancel, Save, Delete
    """

    plan_id = int(plan_id)
    action = request.POST.get("action")
    plan = Plan.objects.get(id=plan_id)
    form = PlanForm(instance=plan, data=request.POST)

    if (settings.DEBUG):
        log.info("From plan_validate")


    if action == "cancel":
        if plan.name == "New plan":
            plan.delete()
        return redirect(action_album, alb_id=plan.album.id, action="edit", status='1', message="Cancelled plan modification")
    elif action == "delete":
        return redirect(action_plan, plan_id, "delete")
    elif form.is_valid():
        plan = form.save()
        if action == "save":
            return redirect(action_plan, plan_id=plan_id, action="edit", status=1, message="Plan saved")
    else:
        error = True
        message = "Please check your field's validity"
        edit = True
        req = plan.album.sequence.request
        req_id = req.id
        seq_id = plan.album.sequence.id
        alb_id = plan.album.id
        depth_level = 4
        return render(request, "routine_manager/view_plan.html", locals())

@login_required
def create_request(request):
    """
        Create a new request and redirects to the editing action on it
    """

    req = Request.objects.create(pyros_user=request.user, name="New request", is_alert=False, autodeposit=False, complete=False, submitted=False)
    return redirect(action_request, req.id, "edit")

@login_required
def create_sequence(request, req_id):
    """
        Create a new sequence and redirects to the editing action on it
    """

    req = Request.objects.get(id=req_id)
    seq = Sequence.objects.create(request=req, name="New sequence", status=Sequence.INCOMPLETE)
    return redirect(action_sequence, seq.id, "edit")

@login_required
def create_album(request, seq_id):
    """
        Create a new album and redirects to the editing action on it
    """

    seq = Sequence.objects.get(id=seq_id)
    alb = Album.objects.create(sequence=seq, name="New album", complete=False)
    return redirect(action_album, alb.id, "edit")

@login_required
def create_plan(request, alb_id):
    """
        Create a new plan and redirects to the editing action on it
    """

    alb = Album.objects.get(id=alb_id)
    plan = Plan.objects.create(album=alb, name="New plan", complete=False)
    return redirect(action_plan, plan.id, "edit")

@login_required
def submit_request(request, req_id, redir):
    """
        Submits a request and its sequences for scheduling
    """

    if (settings.DEBUG):
        log.info("From submit_request")

    req = Request.objects.get(id=req_id)
    error = False
    if req.complete == False:
        error = True
        message = "A request must be complete to be submitted"
    elif req.submitted == True:
        error = True
        message = "The request is already submitted"

    if error == True:
        return redirect(action_request, req_id=req_id, action="view", status=-1, message=message)

    for seq in req.sequences.all():
        seq.status = Sequence.TOBEPLANNED
        seq.save()
    req.submitted = True
    req.save()

    # TODO : changer le first_schedule ...
    '''
    if settings.USE_CELERY:
        print("WITH CELERY")
        scheduler.tasks.scheduling.delay(first_schedule=True, alert=False)
    else:
    '''
    print("Change 1st schedule")
    scheduler.tasks.scheduling().run(first_schedule=True, alert=False)

    message = "The request was submitted"
    if redir == "action_request":
        return redirect(action_request, req_id=req_id, action="view", status=1, message=message)
    else:
        return redirect(requests_list, status=1, message=message)

@login_required
def unsubmit_request(request, req_id):
    """
        Unsubmits a request and remove its sequences from scheduling
    """

    if (settings.DEBUG):
        log.info("From unsubmit_request")

    req = Request.objects.get(id=req_id)

# TODO: uncomment pour la production
#     if req.sequences.filter(Q(status=Sequence.EXECUTED) | Q(status=Sequence.EXECUTING)).exists():
#         message = "You can't unsubmit a request with executed sequences"
#         return redirect(action_request, req_id=req_id, action="view", status=-1, message=message)

    req.submitted = False
    req.save()
    sequences = req.sequences.filter(Q(status=Sequence.TOBEPLANNED) | Q(status=Sequence.PLANNED) |
                                        Q(status=Sequence.INVALID) | Q(status=Sequence.UNPLANNABLE))
    for seq in sequences:
        seq.status = Sequence.COMPLETE
        seq.save()
    # TODO: uncomment
    # scheduler.tasks.scheduling.delay(first_schedule=True, alert=False)  # TODO : changer le first_schedule ...

    if req.complete == True:
        message = "The request was unsubmitted"
    else:
        message = "The request was unsubmitted because it is incomplete"
    return redirect(action_request, req_id=req_id, action="edit", status=1, message=message)

@login_required
def export_request(request, req_id):
    """
        Create an XML file with the given request, and send a download request to the user
    """

    if (settings.DEBUG):
        log.info("From export_request")

    req = Request.objects.get(id=req_id)
    if req.complete == False:
        message = "Request must be completely valid to be serialized"
        return redirect(action_request, req_id=req_id, action="view", status=-1, message=message)

    file_name = SAVED_REQUESTS_FOLDER + "request" + str(req.id) + ".xml"
    rs = RequestSerializer()
    rs.serialize(req, file_name)

    wrapper = FileWrapper(open(file_name, "r"))
    content_type = mimetypes.guess_type(file_name)[0]

    response = HttpResponse(wrapper, content_type=content_type)
    response['Content-Length'] = os.path.getsize(file_name)
    response['Content-Disposition'] = 'attachment; filename=%s' % smart_str(os.path.basename(file_name))

    return response

@login_required
@csrf_exempt
def import_request(request):
    """
        Ask for a XML file, parse it and create a request in DB
        Don't do anything if there is a single error into the file
    """

    if (settings.DEBUG):
        log.info("From import_request")

    log.info(request)

    if request.method == "POST":
        file = request.FILES.get("request_file")
        if file is None:
            status = -1
            message = "File does not exist"
        elif file.size > 1000000:
            status = -1
            message = "File is too big (more than 1 000 000 bytes)"
        else:
            rs = RequestSerializer()
            message = rs.unserialize(file.read(), request.user)
            if message != "":
                status = -1
            else:
                status = 1
                message = "Request imported successfully. Please check it before submitting for scheduling."
        print("message is", message)
    else:
        status = -1
        message = "Internal error"

    # Now, display the list of requests (updated with this new request)
    return redirect(requests_list, status=status, message=message)