import falcon
import logging
import os
import re
import xattr
from datetime import datetime
from certidude import config, push
from certidude.decorators import serialize
from .utils import AuthorityHandler
from .utils.firewall import login_required, authorize_admin, authorize_server

logger = logging.getLogger(__name__)

# TODO: lease namespacing (?)

class LeaseDetailResource(AuthorityHandler):
    @serialize
    @login_required
    @authorize_admin
    def on_get(self, req, resp, cn):
        try:
            path, buf, cert, signed, expires = self.authority.get_signed(cn)
            return dict(
                last_seen =     xattr.getxattr(path, "user.lease.last_seen").decode("ascii"),
                inner_address = xattr.getxattr(path, "user.lease.inner_address").decode("ascii"),
                outer_address = xattr.getxattr(path, "user.lease.outer_address").decode("ascii")
            )
        except EnvironmentError: # Certificate or attribute not found
            raise falcon.HTTPNotFound()


class LeaseResource(AuthorityHandler):
    @authorize_server
    def on_post(self, req, resp):
        client_common_name = req.get_param("client", required=True)
        m = re.match("^(.*, )*CN=(.+?)(, .*)*$", client_common_name) # It's actually DN, resolve it to CN
        if m:
            _, client_common_name, _ = m.groups()

        path, buf, cert, signed, expires = self.authority.get_signed(client_common_name) # TODO: catch exceptions
        if req.get_param("serial") and cert.serial_number != req.get_param_as_int("serial"): # OCSP-ish solution for OpenVPN, not exposed for StrongSwan
            raise falcon.HTTPForbidden("Forbidden", "Invalid serial number supplied")
        now = datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%S.%f")[:-3] + "Z"

        xattr.setxattr(path, "user.lease.outer_address", req.get_param("outer_address", required=True).encode("ascii"))
        xattr.setxattr(path, "user.lease.inner_address", req.get_param("inner_address", required=True).encode("ascii"))
        xattr.setxattr(path, "user.lease.last_seen", now)
        push.publish("lease-update", client_common_name)

        server_common_name = req.context.get("machine")
        path = os.path.join(config.SIGNED_DIR, server_common_name + ".pem")
        xattr.setxattr(path, "user.lease.outer_address", "")
        xattr.setxattr(path, "user.lease.inner_address", "%s" % req.context.get("remote_addr"))
        xattr.setxattr(path, "user.lease.last_seen", now)
        push.publish("lease-update", server_common_name)

        # client-disconnect is pretty much unusable:
        # - Android Connect Client results "IP packet with unknown IP version=2" on gateway
        # - NetworkManager just kills OpenVPN client, disconnect is never reported
        # - Disconnect is also not reported when uplink connection dies or laptop goes to sleep
