from rest_framework.decorators import api_view
from rest_framework.response import Response
from rest_framework.reverse import reverse
from rest_framework import serializers
from rest_framework import generics
from rest_framework import viewsets
from rest_framework.decorators import detail_route, list_route
from rest_framework.views import APIView
from core.models import *
from django.forms import widgets
from django.conf.urls import patterns, url
from services.cord.models import VOLTTenant, VBNGTenant, CordSubscriberRoot
from core.xoslib.objects.cordsubscriber import CordSubscriber
from plus import PlusSerializerMixin, XOSViewSet
from django.shortcuts import get_object_or_404
from xos.apibase import XOSListCreateAPIView, XOSRetrieveUpdateDestroyAPIView, XOSPermissionDenied
from xos.exceptions import *
import json
import subprocess

if hasattr(serializers, "ReadOnlyField"):
    # rest_framework 3.x
    ReadOnlyField = serializers.ReadOnlyField
else:
    # rest_framework 2.x
    ReadOnlyField = serializers.Field

class CordSubscriberIdSerializer(serializers.ModelSerializer, PlusSerializerMixin):
        id = ReadOnlyField()
        service_specific_id = ReadOnlyField()
        vlan_id = ReadOnlyField()      # XXX remove this
        c_tag = ReadOnlyField()
        s_tag = ReadOnlyField()
        vcpe_id = ReadOnlyField()
        instance = ReadOnlyField()
        image = ReadOnlyField()
        vbng_id = ReadOnlyField()
        firewall_enable = serializers.BooleanField()
        firewall_rules = serializers.CharField()
        url_filter_enable = serializers.BooleanField()
        url_filter_rules = serializers.CharField()
        url_filter_level = serializers.CharField(required=False)
        cdn_enable = serializers.BooleanField()
        instance_name = ReadOnlyField()
        image_name = ReadOnlyField()
        routeable_subnet = serializers.CharField(required=False)
        ssh_command = ReadOnlyField()
        bbs_account = ReadOnlyField()

        wan_container_ip = ReadOnlyField()
        uplink_speed = serializers.CharField(required=False)
        downlink_speed = serializers.CharField(required=False)
        status = serializers.CharField()
        enable_uverse = serializers.BooleanField()

        lan_ip = ReadOnlyField()
        wan_ip = ReadOnlyField()
        nat_ip = ReadOnlyField()
        private_ip = ReadOnlyField()

        wan_mac = ReadOnlyField()

        vcpe_synced = serializers.BooleanField()

        humanReadableName = serializers.SerializerMethodField("getHumanReadableName")

        class Meta:
            model = CordSubscriber
            fields = ('humanReadableName', 'id',
                      'service_specific_id', 'vlan_id', 's_tag', 'c_tag',
                      'vcpe_id', 'instance', 'instance_name', 'image', 'image_name',
                      'firewall_enable', 'firewall_rules',
                      'url_filter_enable', 'url_filter_rules', 'url_filter_level',
                      'bbs_account',
                      'ssh_command',
                      'vcpe_synced',
                      'cdn_enable', 'vbng_id', 'routeable_subnet', 'nat_ip', 'lan_ip', 'wan_ip', 'private_ip', 'wan_mac',
                      'wan_container_ip',
                      'uplink_speed', 'downlink_speed', 'status', 'enable_uverse')


        def getHumanReadableName(self, obj):
            return obj.__unicode__()

#------------------------------------------------------------------------------
# The "old" API
# This is used by the xoslib-based GUI
#------------------------------------------------------------------------------

class CordSubscriberList(XOSListCreateAPIView):
    queryset = CordSubscriber.get_tenant_objects().select_related().all()
    serializer_class = CordSubscriberIdSerializer

    method_kind = "list"
    method_name = "cordsubscriber"

class CordSubscriberDetail(XOSRetrieveUpdateDestroyAPIView):
    queryset = CordSubscriber.get_tenant_objects().select_related().all()
    serializer_class = CordSubscriberIdSerializer

    method_kind = "detail"
    method_name = "cordsubscriber"

# We fake a user object by pulling the user data struct out of the
# subscriber object...

def serialize_user(subscriber, user):
    return {"id": "%d-%d" % (subscriber.id, user["id"]),
            "name": user["name"],
            "level": user.get("level",""),
            "mac": user.get("mac", ""),
            "subscriber": subscriber.id }

class CordUserList(APIView):
    method_kind = "list"
    method_name = "corduser"

    def get(self, request, format=None):
        instances=[]
        for subscriber in CordSubscriber.get_tenant_objects().all():
            for user in subscriber.users:
                instances.append( serialize_user(subscriber, user) )

        return Response(instances)

    def post(self, request, format=None):
        data = request.DATA
        subscriber = CordSubscriber.get_tenant_objects().get(id=int(data["subscriber"]))
        user = subscriber.create_user(name=data["name"],
                                    level=data["level"],
                                    mac=data["mac"])
        subscriber.save()

        return Response(serialize_user(subscriber,user))

class CordUserDetail(APIView):
    method_kind = "detail"
    method_name = "corduser"

    def get(self, request, format=None, pk=0):
        parts = pk.split("-")
        subscriber = CordSubscriber.get_tenant_objects().filter(id=parts[0])
        for user in subscriber.users:
            return Response( [ serialize_user(subscriber, user) ] )
        raise XOSNotFound("Failed to find user %s" % pk)

    def delete(self, request, pk):
        parts = pk.split("-")
        subscriber = CordSubscriber.get_tenant_objects().get(id=int(parts[0]))
        subscriber.delete_user(parts[1])
        subscriber.save()
        return Response("okay")

    def put(self, request, pk):
        kwargs={}
        if "name" in request.DATA:
             kwargs["name"] = request.DATA["name"]
        if "level" in request.DATA:
             kwargs["level"] = request.DATA["level"]
        if "mac" in request.DATA:
             kwargs["mac"] = request.DATA["mac"]

        parts = pk.split("-")
        subscriber = CordSubscriber.get_tenant_objects().get(id=int(parts[0]))
        user = subscriber.update_user(parts[1], **kwargs)
        subscriber.save()
        return Response(serialize_user(subscriber,user))

#------------------------------------------------------------------------------
# The "new" API with many more REST endpoints.
# This is for integration with with the subscriber GUI
#------------------------------------------------------------------------------

class CordSubscriberViewSet(XOSViewSet):
    base_name = "subscriber"
    method_name = "rs/subscriber"
    method_kind = "viewset"
    queryset = CordSubscriber.get_tenant_objects().select_related().all()
    serializer_class = CordSubscriberIdSerializer

    def get_vcpe(self):
        subscriber = self.get_object()
        if not subscriber.vcpe:
            raise XOSMissingField("vCPE object is not present for subscriber")
        return subscriber.vcpe

    @classmethod
    def get_urlpatterns(self):
        patterns = super(CordSubscriberViewSet, self).get_urlpatterns()
        patterns.append( self.detail_url("vcpe_synced/$", {"get": "get_vcpe_synced"}, "vcpe_synced") )
        patterns.append( self.detail_url("url_filter/$", {"get": "get_url_filter"}, "url_filter") )
        patterns.append( self.detail_url("url_filter/(?P<level>[a-zA-Z0-9\-_]+)/$", {"put": "set_url_filter"}, "url_filter") )
        patterns.append( self.detail_url("services/$", {"get": "get_services"}, "services") )
        patterns.append( self.detail_url("services/(?P<service>[a-zA-Z0-9\-_]+)/$", {"get": "get_service"}, "get_service") )
        patterns.append( self.detail_url("services/(?P<service>[a-zA-Z0-9\-_]+)/true/$", {"put": "enable_service"}, "enable_service") )
        patterns.append( self.detail_url("services/(?P<service>[a-zA-Z0-9\-_]+)/false/$", {"put": "disable_service"}, "disable_service") )

        patterns.append( self.detail_url("users/$", {"get": "get_users", "post": "create_user"}, "users") )
        patterns.append( self.detail_url("users/clearusers/$", {"get": "clear_users", "put": "clear_users", "post": "clear_users"}, "clearusers") )
        patterns.append( self.detail_url("users/newuser/$", {"put": "create_user", "post": "create_user"}, "newuser") )
        patterns.append( self.detail_url("users/(?P<uid>[0-9\-]+)/$", {"delete": "delete_user"}, "user") )
        patterns.append( self.detail_url("users/(?P<uid>[0-9\-]+)/url_filter/$", {"get": "get_user_level"}, "user_level") )
        patterns.append( self.detail_url("users/(?P<uid>[0-9\-]+)/url_filter/(?P<level>[a-zA-Z0-9\-_]+)/$", {"put": "set_user_level"}, "set_user_level") )

        patterns.append( self.detail_url("bbsdump/$", {"get": "get_bbsdump"}, "bbsdump") )

        patterns.append( url("^rs/initdemo/$", self.as_view({"put": "initdemo", "get": "initdemo"}), name="initdemo") )

        patterns.append( url("^rs/subidlookup/(?P<ssid>[0-9\-]+)/$", self.as_view({"get": "ssiddetail"}), name="ssiddetail") )
        patterns.append( url("^rs/subidlookup/$", self.as_view({"get": "ssidlist"}), name="ssidlist") )

        patterns.append( url("^rs/vbng_mapping/$", self.as_view({"get": "get_vbng_mapping"}), name="vbng_mapping") )

        return patterns

    def list(self, request):
        object_list = self.filter_queryset(self.get_queryset())

        serializer = self.get_serializer(object_list, many=True)

        return Response({"subscribers": serializer.data})

    def get_vcpe_synced(self, request, pk=None):
        subscriber = self.get_object()
        return Response({"vcpe_synced": subscriber.vcpe_synced})

    def get_url_filter(self, request, pk=None):
        subscriber = self.get_object()
        return Response({"level": subscriber.url_filter_level})

    def set_url_filter(self, request, pk=None, level=None):
        subscriber = self.get_object()
        subscriber.url_filter_level = level
        subscriber.save()
        return Response({"level": subscriber.url_filter_level})

    def get_users(self, request, pk=None):
        subscriber = self.get_object()
        return Response(subscriber.users)

    def get_user_level(self, request, pk=None, uid=None):
        subscriber = self.get_object()
        user = subscriber.find_user(uid)
        if user and user.get("level", None):
            level = user["level"]
        else:
            level = self.get_object().url_filter_level

        return Response( {"id": uid, "level": level} )

    def set_user_level(self, request, pk=None, uid=None, level=None):
        subscriber = self.get_object()
        subscriber.update_user(uid, level=level)
        subscriber.save()
        return self.get_user_level(request, pk, uid)

    def create_user(self, request, pk=None):
        data = request.DATA
        name = data.get("name",None)
        mac = data.get("mac",None)
        if (not name):
             raise XOSMissingField("name must be specified when creating user")
        if (not mac):
             raise XOSMissingField("mac must be specified when creating user")

        subscriber = self.get_object()
        newuser = subscriber.create_user(name=name, mac=mac)
        subscriber.save()

        return Response(newuser)

    def delete_user(self, request, pk=None, uid=None):
        subscriber = self.get_object()
        subscriber.delete_user(uid)
        subscriber.save()

        return Response( {"id": uid, "deleted": True} )

    def clear_users(self, request, pk=None):
        subscriber = self.get_object()
        subscriber.users = []
        subscriber.save()

        return Response( "Okay" )

    def get_services(self, request, pk=None):
        subscriber = self.get_object()
        return Response(subscriber.services)

    def get_service(self, request, pk=None, service=None):
        service_attr = service+"_enable"
        subscriber = self.get_object()
        return Response({service: getattr(subscriber, service_attr)})

    def enable_service(self, request, pk=None, service=None):
        service_attr = service+"_enable"
        subscriber = self.get_object()
        setattr(subscriber, service_attr, True)
        subscriber.save()
        return Response({service: getattr(subscriber, service_attr)})

    def disable_service(self, request, pk=None, service=None):
        service_attr = service+"_enable"
        subscriber = self.get_object()
        setattr(subscriber, service_attr, False)
        subscriber.save()
        return Response({service: getattr(subscriber, service_attr)})

    def get_bbsdump(self, request, pk=None):
        subscriber = self.get_object()
        if not subsciber.volt or not subscriber.volt.vcpe:
            raise XOSMissingField("subscriber has no vCPE")
        if not subscriber.volt.vcpe.bbs_account:
            raise XOSMissingField("subscriber has no bbs_account")

        result=subprocess.check_output(["python", "/opt/xos/observers/vcpe/broadbandshield.py", "dump", subscriber.volt.vcpe.bbs_account, "123"])
        if request.GET.get("theformat",None)=="text":
            from django.http import HttpResponse
            return HttpResponse(result, content_type="text/plain")
        else:
            return Response( {"bbs_dump": result } )

    def setup_demo_subscriber(self, subscriber):
        # nuke the users and start over
        subscriber.users = []
        subscriber.create_user(name="Mom's PC",      mac="010203040506", level="PG_13")
        subscriber.create_user(name="Dad's PC",      mac="90E2Ba82F975", level="PG_13")
        subscriber.create_user(name="Jack's Laptop", mac="685B359D91D5", level="PG_13")
        subscriber.create_user(name="Jill's Laptop", mac="34363BC9B6A6", level="PG_13")
        subscriber.save()

    def initdemo(self, request):
        object_list = CordSubscriber.get_tenant_objects().all()

        # reset the parental controls in any existing demo vCPEs
        for o in object_list:
            if str(o.service_specific_id) in ["0", "1"]:
                self.setup_demo_subscriber(o)

        demo_subscribers = [o for o in object_list if o.is_demo_user]

        if demo_subscribers:
            return Response({"id": demo_subscribers[0].id})

        subscriber = CordSubscriberRoot(service_specific_id=1234,
                                        name="demo-subscriber",)
        subscriber.is_demo_user = True
        subscriber.save()

        self.setup_demo_subscriber(subscriber)

        return Response({"id": subscriber.id})

    def ssidlist(self, request):
        object_list = CordSubscriber.get_tenant_objects().all()

        ssidmap = [ {"service_specific_id": x.service_specific_id, "subscriber_id": x.id} for x in object_list ]

        return Response({"ssidmap": ssidmap})

    def ssiddetail(self, pk=None, ssid=None):
        object_list = CordSubscriber.get_tenant_objects().all()

        ssidmap = [ {"service_specific_id": x.service_specific_id, "subscriber_id": x.id} for x in object_list if str(x.service_specific_id)==str(ssid) ]

        if len(ssidmap)==0:
            raise XOSNotFound("didn't find ssid %s" % str(ssid))

        return Response( ssidmap[0] )

    def get_vbng_mapping(self, request):
        object_list = VBNGTenant.get_tenant_objects().all()

        mappings = []
        for vbng in object_list:
            if vbng.mapped_ip and vbng.routeable_subnet:
                mappings.append( {"private_ip": vbng.mapped_ip, "routeable_subnet": vbng.routeable_subnet, "mac": vbng.mapped_mac, "hostname": vbng.mapped_hostname} )

        return Response( {"vbng_mapping": mappings} )

class CordDebugIdSerializer(serializers.ModelSerializer, PlusSerializerMixin):
    # Swagger is failing because CordDebugViewSet has neither a model nor
    # a serializer_class. Stuck this in here as a placeholder for now.
    id = ReadOnlyField()
    class Meta:
        model = CordSubscriber

class CordDebugViewSet(XOSViewSet):
    base_name = "cord_debug"
    method_name = "rs/cord_debug"
    method_kind = "viewset"
    serializer_class = CordDebugIdSerializer

    @classmethod
    def get_urlpatterns(self):
        patterns = []
        patterns.append( url("^rs/cord_debug/vbng_dump/$", self.as_view({"get": "get_vbng_dump"}), name="vbng_dump"))
        return patterns

    # contact vBNG service and dump current list of mappings
    def get_vbng_dump(self, request, pk=None):
        result=subprocess.check_output(["curl", "http://10.0.3.136:8181/onos/virtualbng/privateip/map"])
        if request.GET.get("theformat",None)=="text":
            from django.http import HttpResponse
            result = json.loads(result)["map"]

            lines = []
            for row in result:
                for k in row.keys():
                     lines.append( "%s %s" % (k, row[k]) )

            return HttpResponse("\n".join(lines), content_type="text/plain")
        else:
            return Response( {"vbng_dump": json.loads(result)["map"] } )
