__filename__ = "skills.py"
__author__ = "Bob Mottram"
__license__ = "AGPL3+"
__version__ = "1.2.0"
__maintainer__ = "Bob Mottram"
__email__ = "bob@libreserver.org"
__status__ = "Production"
__module_group__ = "Profile Metadata"

import os
from webfinger import webfingerHandle
from auth import createBasicAuthHeader
from posts import getPersonBox
from session import postJson
from utils import hasObjectString
from utils import get_full_domain
from utils import getNicknameFromActor
from utils import getDomainFromActor
from utils import loadJson
from utils import getOccupationSkills
from utils import setOccupationSkillsList
from utils import acct_dir
from utils import local_actor_url
from utils import hasActor


def setSkillsFromDict(actor_json: {}, skillsDict: {}) -> []:
    """Converts a dict containing skills to a list
    Returns the string version of the dictionary
    """
    skillsList = []
    for name, value in skillsDict.items():
        skillsList.append(name + ':' + str(value))
    setOccupationSkillsList(actor_json, skillsList)
    return skillsList


def getSkillsFromList(skillsList: []) -> {}:
    """Returns a dict of skills from a list
    """
    if isinstance(skillsList, list):
        skillsList2 = skillsList
    else:
        skillsList2 = skillsList.split(',')
    skillsDict = {}
    for skill in skillsList2:
        if ':' not in skill:
            continue
        name = skill.split(':')[0].strip().lower()
        valueStr = skill.split(':')[1]
        if not valueStr.isdigit():
            continue
        skillsDict[name] = int(valueStr)
    return skillsDict


def actorSkillValue(actor_json: {}, skillName: str) -> int:
    """Returns The skill level from an actor
    """
    ocSkillsList = getOccupationSkills(actor_json)
    skillsDict = getSkillsFromList(ocSkillsList)
    if not skillsDict:
        return 0
    skillName = skillName.lower()
    if skillsDict.get(skillName):
        return skillsDict[skillName]
    return 0


def noOfActorSkills(actor_json: {}) -> int:
    """Returns the number of skills that an actor has
    """
    if actor_json.get('hasOccupation'):
        skillsList = getOccupationSkills(actor_json)
        return len(skillsList)
    return 0


def setActorSkillLevel(actor_json: {},
                       skill: str, skillLevelPercent: int) -> bool:
    """Set a skill level for a person
    Setting skill level to zero removes it
    """
    if skillLevelPercent < 0 or skillLevelPercent > 100:
        return False

    if not actor_json:
        return True
    if not actor_json.get('hasOccupation'):
        actor_json['hasOccupation'] = [{
            '@type': 'Occupation',
            'name': '',
            "occupationLocation": {
                "@type": "City",
                "name": "Fediverse"
            },
            'skills': []
        }]
    ocSkillsList = getOccupationSkills(actor_json)
    skillsDict = getSkillsFromList(ocSkillsList)
    if not skillsDict.get(skill):
        if len(skillsDict.items()) >= 32:
            print('WARN: Maximum number of skills reached for ' +
                  actor_json['id'])
            return False
    if skillLevelPercent > 0:
        skillsDict[skill] = skillLevelPercent
    else:
        if skillsDict.get(skill):
            del skillsDict[skill]
    setSkillsFromDict(actor_json, skillsDict)
    return True


def setSkillLevel(base_dir: str, nickname: str, domain: str,
                  skill: str, skillLevelPercent: int) -> bool:
    """Set a skill level for a person
    Setting skill level to zero removes it
    """
    if skillLevelPercent < 0 or skillLevelPercent > 100:
        return False
    actorFilename = acct_dir(base_dir, nickname, domain) + '.json'
    if not os.path.isfile(actorFilename):
        return False

    actor_json = loadJson(actorFilename)
    return setActorSkillLevel(actor_json,
                              skill, skillLevelPercent)


def getSkills(base_dir: str, nickname: str, domain: str) -> []:
    """Returns the skills for a given person
    """
    actorFilename = acct_dir(base_dir, nickname, domain) + '.json'
    if not os.path.isfile(actorFilename):
        return False

    actor_json = loadJson(actorFilename)
    if actor_json:
        if not actor_json.get('hasOccupation'):
            return None
        ocSkillsList = getOccupationSkills(actor_json)
        return getSkillsFromList(ocSkillsList)
    return None


def outboxSkills(base_dir: str, nickname: str, message_json: {},
                 debug: bool) -> bool:
    """Handles receiving a skills update
    """
    if not message_json.get('type'):
        return False
    if not message_json['type'] == 'Skill':
        return False
    if not hasActor(message_json, debug):
        return False
    if not hasObjectString(message_json, debug):
        return False

    actorNickname = getNicknameFromActor(message_json['actor'])
    if actorNickname != nickname:
        return False
    domain, port = getDomainFromActor(message_json['actor'])
    skill = message_json['object'].replace('"', '').split(';')[0].strip()
    skillLevelPercentStr = \
        message_json['object'].replace('"', '').split(';')[1].strip()
    skillLevelPercent = 50
    if skillLevelPercentStr.isdigit():
        skillLevelPercent = int(skillLevelPercentStr)

    return setSkillLevel(base_dir, nickname, domain,
                         skill, skillLevelPercent)


def sendSkillViaServer(base_dir: str, session, nickname: str, password: str,
                       domain: str, port: int,
                       http_prefix: str,
                       skill: str, skillLevelPercent: int,
                       cached_webfingers: {}, person_cache: {},
                       debug: bool, project_version: str,
                       signing_priv_key_pem: str) -> {}:
    """Sets a skill for a person via c2s
    """
    if not session:
        print('WARN: No session for sendSkillViaServer')
        return 6

    domain_full = get_full_domain(domain, port)

    actor = local_actor_url(http_prefix, nickname, domain_full)
    toUrl = actor
    ccUrl = actor + '/followers'

    if skillLevelPercent:
        skillStr = skill + ';' + str(skillLevelPercent)
    else:
        skillStr = skill + ';0'

    newSkillJson = {
        'type': 'Skill',
        'actor': actor,
        'object': '"' + skillStr + '"',
        'to': [toUrl],
        'cc': [ccUrl]
    }

    handle = http_prefix + '://' + domain_full + '/@' + nickname

    # lookup the inbox for the To handle
    wfRequest = \
        webfingerHandle(session, handle, http_prefix,
                        cached_webfingers,
                        domain, project_version, debug, False,
                        signing_priv_key_pem)
    if not wfRequest:
        if debug:
            print('DEBUG: skill webfinger failed for ' + handle)
        return 1
    if not isinstance(wfRequest, dict):
        print('WARN: skill webfinger for ' + handle +
              ' did not return a dict. ' + str(wfRequest))
        return 1

    postToBox = 'outbox'

    # get the actor inbox for the To handle
    originDomain = domain
    (inboxUrl, pubKeyId, pubKey, fromPersonId, sharedInbox, avatarUrl,
     displayName, _) = getPersonBox(signing_priv_key_pem,
                                    originDomain,
                                    base_dir, session, wfRequest,
                                    person_cache, project_version,
                                    http_prefix, nickname, domain,
                                    postToBox, 76121)

    if not inboxUrl:
        if debug:
            print('DEBUG: skill no ' + postToBox +
                  ' was found for ' + handle)
        return 3
    if not fromPersonId:
        if debug:
            print('DEBUG: skill no actor was found for ' + handle)
        return 4

    authHeader = createBasicAuthHeader(nickname, password)

    headers = {
        'host': domain,
        'Content-type': 'application/json',
        'Authorization': authHeader
    }
    postResult = \
        postJson(http_prefix, domain_full,
                 session, newSkillJson, [], inboxUrl,
                 headers, 30, True)
    if not postResult:
        if debug:
            print('DEBUG: POST skill failed for c2s to ' + inboxUrl)
#        return 5

    if debug:
        print('DEBUG: c2s POST skill success')

    return newSkillJson


def actorHasSkill(actor_json: {}, skillName: str) -> bool:
    """Returns true if the given actor has the given skill
    """
    ocSkillsList = getOccupationSkills(actor_json)
    for skillStr in ocSkillsList:
        if skillName + ':' in skillStr:
            return True
    return False