# Copyright 2014 Budapest University of Technology and Economics (BME IK)
#
# This file is part of CIRCLE Cloud.
#
# CIRCLE is free software: you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation, either version 3 of the License, or (at your option)
# any later version.
#
# CIRCLE is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along
# with CIRCLE.  If not, see <http://www.gnu.org/licenses/>.
from __future__ import unicode_literals, absolute_import

from datetime import timedelta
import json
import logging

from braces.views._access import AccessMixin
from django.contrib import messages
from django.contrib.auth.models import User
from django.contrib.messages.views import SuccessMessageMixin
from django.core.urlresolvers import reverse, reverse_lazy
from django.core.exceptions import PermissionDenied, SuspiciousOperation
from django.db.models import Count
from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import redirect, get_object_or_404
from django.utils import timezone
from django.utils.translation import ugettext as _, ugettext_noop
from django.views import View
from django.views.generic import (
    TemplateView, CreateView, UpdateView,
)

from braces.views import (
    LoginRequiredMixin, PermissionRequiredMixin,
)
from django.views.generic.edit import BaseUpdateView, ModelFormMixin, FormView
from django_tables2 import SingleTableView
from keystoneauth1 import session
from keystoneauth1.identity import v3
from openstack_auth.utils import fix_auth_url_version

from vm.models import (
    InstanceTemplate, InterfaceTemplate, Instance, Lease, InstanceActivity
)
from storage.models import Disk

from ..forms import (
    TemplateForm, TemplateListSearchForm, AclUserOrGroupAddForm, LeaseForm,
)
from ..tables import TemplateListTable, LeaseListTable

from .util import (
    AclUpdateView, FilterMixin,
    # TransferOwnershipConfirmView, TransferOwnershipView,
    # DeleteViewBase,
    GraphMixin,
    DeleteViewBase, TransferOwnershipConfirmView, TransferOwnershipView)

logger = logging.getLogger(__name__)
from django.conf import settings


class TemplateChoose(LoginRequiredMixin, TemplateView):

    def get_template_names(self):
        if self.request.is_ajax():
            return ['dashboard/_modal.html']
        else:
            return ['dashboard/nojs-wrapper.html']

    def get_context_data(self, *args, **kwargs):
        context = super(TemplateChoose, self).get_context_data(*args, **kwargs)
        templates = InstanceTemplate.get_objects_with_level("user",
                                                            self.request.user)
        context.update({
            'box_title': _('Choose template'),
            'ajax_title': True,
            'template': "dashboard/_template-choose.html",
            'templates': templates.all(),
        })
        return context

    def post(self, request, *args, **kwargs):
        if not request.user.has_perm('vm.create_template'):
            raise PermissionDenied()

        template = request.POST.get("parent")
        if template == "base_vm":
            return redirect(reverse("dashboard.views.template-create"))
        elif template is None:
            messages.warning(request, _("Select an option to proceed."))
            return redirect(reverse("dashboard.views.template-choose"))
        else:
            template = get_object_or_404(InstanceTemplate, pk=template)

        if not template.has_level(request.user, "user"):
            raise PermissionDenied()

        instance = Instance.create_from_template(
            template=template, owner=request.user, is_base=True)

        return redirect(instance.get_absolute_url())


class TemplateCreate(SuccessMessageMixin, CreateView):
    model = InstanceTemplate
    form_class = TemplateForm

    def get_template_names(self):
        if self.request.is_ajax():
            pass
        else:
            return ['dashboard/nojs-wrapper.html']

    def get_context_data(self, *args, **kwargs):
        context = super(TemplateCreate, self).get_context_data(*args, **kwargs)

        num_leases = Lease.get_objects_with_level("operator",
                                                  self.request.user).count()
        can_create_leases = self.request.user.has_perm("create_leases")
        context.update({
            'box_title': _("Create a new base VM"),
            'template': "dashboard/_template-create.html",
            'show_lease_create': num_leases < 1 and can_create_leases
        })
        return context

    def get(self, *args, **kwargs):
        if not self.request.user.has_perm('vm.create_base_template'):
            raise PermissionDenied()

        return super(TemplateCreate, self).get(*args, **kwargs)

    def get_form_kwargs(self):
        kwargs = super(TemplateCreate, self).get_form_kwargs()
        kwargs['user'] = self.request.user
        return kwargs

    def post(self, request, *args, **kwargs):
        if not self.request.user.has_perm('vm.create_base_template'):
            raise PermissionDenied()

        form = self.form_class(request.POST, user=request.user)
        if not form.is_valid():
            return self.get(request, form, *args, **kwargs)
        else:
            post = form.cleaned_data
            networks = self.__create_networks(post.pop("networks"),
                                              request.user)
            post.pop("parent")
            post['max_ram_size'] = post['ram_size']
            req_traits = post.pop("req_traits")
            tags = post.pop("tags")
            post['pw'] = User.objects.make_random_password()
            post['is_base'] = True
            inst = Instance.create(params=post, disks=[],
                                   networks=networks,
                                   tags=tags, req_traits=req_traits)

            return HttpResponseRedirect("%s#resources" %
                                        inst.get_absolute_url())

    def __create_networks(self, vlans, user):
        networks = []
        for v in vlans:
            if not v.has_level(user, "user"):
                raise PermissionDenied()
            networks.append(InterfaceTemplate(vlan=v, managed=v.managed))
        return networks

    def get_success_url(self):
        return reverse_lazy("dashboard.views.template-list")


class TemplateAclUpdateView(AclUpdateView):
    model = InstanceTemplate


class TemplateList(LoginRequiredMixin, FilterMixin, SingleTableView):
    template_name = "dashboard/template-list.html"
    model = InstanceTemplate
    table_class = TemplateListTable
    table_pagination = False

    allowed_filters = {
        'name': "name__icontains",
        'tags[]': "tags__name__in",
        'tags': "tags__name__in",  # for search string
        'owner': "owner__username",
        'ram': "ram_size",
        'ram_size': "ram_size",
        'cores': "num_cores",
        'num_cores': "num_cores",
        'access_method': "access_method__iexact",
    }

    def get_context_data(self, *args, **kwargs):
        context = super(TemplateList, self).get_context_data(*args, **kwargs)
        user = self.request.user

        from django.utils.module_loading import import_string
        check = import_string("openstack_auth.policy.check")

        os_policy_actions = (("circle", "lease:manage"),)
        has_lease_rights = check(os_policy_actions, self.request)

        leases = Lease.objects.all()
        context['lease_table'] = LeaseListTable(
            leases, request=self.request,
            template="django_tables2/table_no_page.html",
        )
        context['show_lease_table'] = has_lease_rights

        context['search_form'] = self.search_form

        #TODO: what is this?

        # tem0for t in InstanceTemplate.objects.all()
        # #  if t.instance_set.count() < 1]
        # never_instantiated = context['object_list'].annotate(
        #     instance_count=Count("instance_set")).filter(instance_count__lt=1)
        #
        # # templates without active virtual machines
        # active_statuses = Instance.STATUS._db_values - set(["DESTROYED"])
        # templates_wo_instances = context['object_list'].exclude(
        #     pk__in=InstanceTemplate.objects.filter(
        #         instance_set__status__in=active_statuses)
        # ).exclude(pk__in=never_instantiated)
        #
        # def get_create_acts_younger_than(days):
        #     return InstanceActivity.objects.filter(
        #         activity_code="vm.Instance.create",
        #         finished__gt=timezone.now() - timedelta(days=days))
        #
        # # templates without active virtual machines
        # # last machine started later than 90 days
        # templates_wo_i_90 = templates_wo_instances.exclude(
        #     instance_set__activity_log__in=get_create_acts_younger_than(90))
        #
        # # templates without active virtual machines
        # # last machine started later than 180 days
        # templates_wo_i_180 = templates_wo_instances.exclude(
        #     instance_set__activity_log__in=get_create_acts_younger_than(180))
        #
        # context['unused_templates'] = {
        #     'never_instantiated': never_instantiated,
        #     'templates_wo_instances': templates_wo_instances,
        #     'templates_wo_instances_90': templates_wo_i_90,
        #     'templates_wo_instances_180': templates_wo_i_180,
        # }

        return context

    def get(self, *args, **kwargs):
        self.search_form = TemplateListSearchForm(self.request.GET)
        self.search_form.full_clean()
        if self.request.is_ajax():
            templates = [
                {
                    'icon': i.os_type,
                    'system': i.system,
                    'url': reverse("dashboard.views.template-detail",
                                   kwargs={'pk': i.pk}),
                    'name': i.name
                } for i in self.get_queryset()]
            return HttpResponse(
                json.dumps(templates),
                content_type="application/json",
            )
        else:
            return super(TemplateList, self).get(*args, **kwargs)

    def get_queryset(self):
        return InstanceTemplate.objects.filter(owner_id=self.request.user.id)

class TemplateDelete(DeleteViewBase):
    model = InstanceTemplate
    success_message = _("Template successfully deleted.")

    def get_success_url(self):
        return reverse("dashboard.views.template-list")

    def delete_obj(self, request, *args, **kwargs):
        object = self.get_object()
        object.destroy_disks()
        object.delete()




class TemplateDetail(LoginRequiredMixin, GraphMixin, SuccessMessageMixin, UpdateView):
    model = InstanceTemplate
    template_name = "dashboard/template-edit.html"
    form_class = TemplateForm
    success_message = _("Successfully modified template.")

    def get(self, request, *args, **kwargs):
        template = self.get_object()

        #TODO: multiple users
        if template.owner_id != request.user.id:
            raise PermissionDenied()

        if request.is_ajax():
            template = {
                'num_cores': template.num_cores,
                'ram_size': template.ram_size,
                'priority': template.priority,
                'arch': template.arch,
                'description': template.description,
                'system': template.system,
                'name': template.name,
                'disks': [{'pk': d.pk, 'name': d.name}
                          for d in template.disks.all()],
                'network': [
                    {'vlan_pk': i.vlan.pk, 'vlan': i.vlan.name,
                     'managed': i.managed}
                    for i in InterfaceTemplate.objects.filter(
                        template=self.get_object()).all()
                ]
            }
            return HttpResponse(json.dumps(template),
                                content_type="application/json")
        else:
            return super(TemplateDetail, self).get(request, *args, **kwargs)

    def __get_glance_admin_client(self, request):
        from keystoneauth1 import session
        from glanceclient import Client

        auth = v3.Password(
            auth_url=fix_auth_url_version(settings.OPENSTACK_KEYSTONE_URL),
            user_id=settings.OPENSTACK_CIRCLE_USERID,
            password=settings.OPENSTACK_CIRCLE_PASSWORD,
            project_id=request.user.tenant_id,
        )
        session = session.Session(auth=auth, verify=False)

        return Client('2', session=session)

    def __get_members_shared_with(self):
        template = self.get_object()
        glance = self.__get_glance_admin_client(self.request)
        members_generator = glance.image_members.list(template.image_id)
        return [m for m in members_generator]

    def __get_project_client(self, project_id):
        from keystoneclient.v3 import client

        auth = v3.Password(
            auth_url=fix_auth_url_version(settings.OPENSTACK_KEYSTONE_URL),
            user_id=settings.OPENSTACK_CIRCLE_USERID,
            password=settings.OPENSTACK_CIRCLE_PASSWORD,
            project_id=project_id,
        )
        sess = session.Session(auth=auth, verify=False)
        return client.Client(session=sess, interface='public')

    def __get_username_for(self, project_id):
        client = self.__get_project_client(project_id)
        project = client.projects.get(project_id)
        return project.name  # as username = project's name

    def __get_users_shared_with(self):
        members = self.__get_members_shared_with()
        for m in members:
            m['username'] = self.__get_username_for(m.member_id)
        return members

    def get_context_data(self, **kwargs):
        template = self.get_object()
        context = super(TemplateDetail, self).get_context_data(**kwargs)
        # context['acl'] = AclUpdateView.get_acl_data(
        #     template, self.request.user, 'dashboard.views.template-acl')
        context['manage_access_url'] = reverse_lazy('dashboard.views.template-acl',
                                                    kwargs={'pk': template.id})
        context['shared_with_users'] = self.__get_users_shared_with()
        # context['disks'] = template.disks.all()
        context['is_owner'] = template.owner_id == self.request.user.id
        context['aclform'] = AclUserOrGroupAddForm()
        # context['parent'] = template.parent
        # context['show_graph'] = template.has_level(self.request.user, 'operator')
        return context

    def get_success_url(self):
        return reverse_lazy("dashboard.views.template-detail",
                            kwargs=self.kwargs)

    def post(self, request):
        template = self.get_object()
        # TODO: multiple users
        if template.owner_id != request.user.id:
            raise PermissionDenied()
        return super(TemplateDetail, self).post(self, request, args, kwargs)

    def get_form_kwargs(self, *args, **kwargs):
        kwargs = super(TemplateDetail, self).get_form_kwargs()
        kwargs['user'] = self.request.user
        return kwargs

#
# class DiskRemoveView(DeleteViewBase):
#     model = Disk
#     success_message = _("Disk successfully removed.")
#
#     def get_queryset(self):
#         qs = super(DiskRemoveView, self).get_queryset()
#         return qs.exclude(template_set=None)
#
#     def check_auth(self):
#         disk = self.get_object()
#         template = disk.template_set.get()
#         if not template.has_level(self.request.user, 'owner'):
#             raise PermissionDenied()
#
#     def get_context_data(self, **kwargs):
#         disk = self.get_object()
#         template = disk.template_set.get()
#         context = super(DiskRemoveView, self).get_context_data(**kwargs)
#         context['title'] = _("Disk remove confirmation")
#         context['text'] = _("Are you sure you want to remove "
#                             "<strong>%(disk)s</strong> from "
#                             "<strong>%(app)s</strong>?" % {'disk': disk,
#                                                            'app': template}
#                             )
#         return context
#
#     def delete_obj(self, request, *args, **kwargs):
#         disk = self.get_object()
#         template = disk.template_set.get()
#         template.remove_disk(disk)
#         disk.destroy()
#
#     def get_success_url(self):
#         return self.request.POST.get("next") or "/"

class PolicyMixin(AccessMixin):
    os_policy_actions = None

    def has_permission(self):
        if self.os_policy_actions:
            from django.utils.module_loading import import_string
            check = import_string("openstack_auth.policy.check")

            return check(self.os_policy_actions, self.request)
        return False

    def dispatch(self, request, *args, **kwargs):
        if not self.has_permission():
            return self.handle_no_permission(request)
        return super(PolicyMixin, self).dispatch(request, *args, **kwargs)


class LeaseCreate(LoginRequiredMixin, PolicyMixin,
                  SuccessMessageMixin, CreateView):
    model = Lease
    form_class = LeaseForm
    template_name = "dashboard/lease-create.html"
    success_message = _("Successfully created a new lease.")
    os_policy_actions = (("circle", "lease:manage"),)

    def get_success_url(self):
        return reverse_lazy("dashboard.views.template-list")

    def get_form_kwargs(self):
        from openstack_api import keystone
        kwargs = super(LeaseCreate, self).get_form_kwargs()
        kwargs['roles'] = keystone.role_list(self.request)
        return kwargs


class LeaseDetail(LoginRequiredMixin, PolicyMixin,
                  SuccessMessageMixin, UpdateView):
    model = Lease
    form_class = LeaseForm
    template_name = "dashboard/lease-edit.html"
    success_message = _("Successfully modified lease.")
    os_policy_actions = (("circle", "lease:manage"),)

    def get_success_url(self):
        return reverse_lazy("dashboard.views.lease-detail", kwargs=self.kwargs)

    def get_form_kwargs(self):
        from openstack_api import keystone
        kwargs = super(LeaseDetail, self).get_form_kwargs()
        kwargs['roles'] = keystone.role_list(self.request)
        return kwargs

    def get_context_data(self, *args, **kwargs):
        context = super(LeaseDetail, self).get_context_data(*args, **kwargs)
        context['is_default'] = settings.DEFAULT_LEASE_NAME == self.object.name
        return context


class LeaseDelete(DeleteViewBase):
    model = Lease
    success_message = _("Lease successfully deleted.")
    os_policy_actions = (("circle", "lease:am"),)

    def get_success_url(self):
        return reverse("dashboard.views.template-list")

    def get_context_data(self, *args, **kwargs):
        c = super(LeaseDelete, self).get_context_data(*args, **kwargs)
        lease = self.get_object()
        templates = lease.instancetemplate_set
        if templates.count() > 0:
            text = _("You can't delete this lease because some templates "
                     "are still using it, modify these to proceed: ")

            c['text'] = text + ", ".join("<strong>%s (#%d)</strong>"
                                         "" % (o.name, o.pk)
                                         for o in templates.all())
            c['disable_submit'] = True
        return c

    def delete_obj(self, request, *args, **kwargs):
        object = self.get_object()
        if object.instancetemplate_set.count() > 0:
            raise SuspiciousOperation()
        object.delete()


class TransferTemplateOwnershipConfirmView(TransferOwnershipConfirmView):
    template = "dashboard/confirm/transfer-template-ownership.html"
    model = InstanceTemplate


class TransferTemplateOwnershipView(TransferOwnershipView):
    confirm_view = TransferTemplateOwnershipConfirmView
    model = InstanceTemplate
    notification_msg = ugettext_noop(
        '%(owner)s offered you to take the ownership of '
        'his/her template called %(instance)s. '
        '<a href="%(token)s" '
        'class="btn btn-success btn-small">Accept</a>')
    token_url = 'dashboard.views.template-transfer-ownership-confirm'
    template = "dashboard/template-tx-owner.html"
