# (c) cavaliba.com - data/api - aaa.py

import re

from django.db.models import F
from django.utils import timezone

import app_home.cache as cache
from app_data import crypto
from app_data.data import Instance
from app_data.ip import get_user_ip
from app_data.role import Role
from app_home.models import CavalibaAPIStat


def api_update_error(keyname=None):
    if not keyname:
        return
    CavalibaAPIStat.objects.get_or_create(keyname=keyname)
    CavalibaAPIStat.objects.filter(keyname=keyname).update(
        error_count=F("error_count") + 1, last_error=timezone.now()
    )


def api_update_success(keyname=None):
    if not keyname:
        return
    CavalibaAPIStat.objects.get_or_create(keyname=keyname)
    CavalibaAPIStat.objects.filter(keyname=keyname).update(
        success_count=F("success_count") + 1, last_success=timezone.now()
    )


def check_ip_filter(ip, filter):
    # TODO : ip in ACL filter ?
    return True


def start_api(request, permission=None):

    aaa = {}
    aaa["keyname"] = None
    aaa["is_readonly"] = True
    aaa["error"] = "no action performed"
    aaa["acl"] = None

    aaa["is_trusted_ip"] = False
    aaa["is_anonymous"] = True
    aaa["is_authenticated"] = False
    aaa["is_visitor"] = False
    aaa["is_admin"] = False
    aaa["is_allowed"] = False
    aaa["user"] = None
    aaa["userid"] = None
    aaa["username"] = "API:?"
    aaa["user_ip"] = get_user_ip(request)

    aaa["perms"] = []

    cache.init()

    cavaliba_auth = request.headers.get("X-Cavaliba-Key", "")
    if not cavaliba_auth:
        aaa["error"] = "missing key"
        return aaa

    (keyname, clear_secret) = cavaliba_auth.split()
    m = re.compile(r"[a-zA-Z0-9()_/.-]*$")

    if not keyname:
        aaa["error"] = "invalid key"
        return aaa
    if not m.match(keyname):
        aaa["error"] = "invalid key"
        return aaa

    aaa["keyname"] = keyname

    instance = Instance.from_keyname(classname="_apikey", keyname=keyname)
    if not instance:
        aaa["error"] = "invalid keyname"
        return aaa

    if not instance.is_enabled:
        aaa["error"] = "invalid keyname"
        api_update_error(keyname)
        return aaa

    instance.fields["is_readonly"].get_first_value()

    if not crypto.hash_check(clear_secret, instance.fields["secret"].get_first_value()):
        aaa["error"] = "invalid keyname/secret"
        api_update_error(keyname)
        return aaa

    aaa["is_authenticated"] = True
    aaa["is_readonly"] = instance.fields["is_readonly"].get_first_value()

    user_ip = get_user_ip(request)
    aaa["user_ip"] = user_ip
    ip_filter = instance.fields["ip_filter"].get_first_value()

    if len(ip_filter) > 0:
        r = check_ip_filter(user_ip, ip_filter)
        if not r:
            aaa["error"] = "invalid ip for key"
            api_update_error(keyname)
            return aaa

    try:
        aaa["acl"] = instance.fields["acl"].get_first_value()
    except Exception:
        aaa["acl"] = ""

    aaa["perms"] = []
    perms = []

    try:
        acl_entries = instance.fields["acl"].get_first_value().splitlines()
    except Exception:
        acl_entries = []

    for acl in acl_entries:
        if acl.startswith("!"):
            negate = True
            acl2 = acl[1:]
        else:
            negate = False
            acl2 = acl

        if ":" in acl2:
            if acl2.startswith("role:"):
                rolename = acl2[5:]
                role = Role.from_keyname(keyname=rolename)

                roleperms = []
                if role and role.is_bound:
                    roleperms = [p.keyname for p in role.get_permissions()]

                for p in roleperms:
                    if negate:
                        if p in perms:
                            perms.remove(p)
                    else:
                        if p not in perms:
                            perms.append(p)
        else:
            if len(acl2) > 0:
                if negate:
                    if acl2 in perms:
                        perms.remove(acl2)
                else:
                    if acl2 not in perms:
                        perms.append(acl2)

    aaa["perms"] = list(set(perms))

    if permission:
        if permission not in aaa["perms"]:
            aaa["error"] = "no permission"
            api_update_error(keyname)
            return aaa

    api_update_success(keyname)
    aaa["is_allowed"] = True
    aaa["error"] = ""
    aaa["username"] = "API:" + aaa["keyname"]
    return aaa
