__filename__ = "acceptreject.py"
__author__ = "Bob Mottram"
__license__ = "AGPL3+"
__version__ = "1.1.0"
__maintainer__ = "Bob Mottram"
__email__ = "bob@freedombone.net"
__status__ = "Production"

import os
from utils import urlPermitted
from utils import getDomainFromActor
from utils import getNicknameFromActor
from utils import domainPermitted
from utils import followPerson


def createAcceptReject(baseDir: str, federationList: [],
                       nickname: str, domain: str, port: int,
                       toUrl: str, ccUrl: str, httpPrefix: str,
                       objectJson: {}, acceptType: str) -> {}:
    """Accepts or rejects something (eg. a follow request or offer)
    Typically toUrl will be https://www.w3.org/ns/activitystreams#Public
    and ccUrl might be a specific person favorited or repeated and
    the followers url objectUrl is typically the url of the message,
    corresponding to url or atomUri in createPostBase
    """
    if not objectJson.get('actor'):
        return None

    if not urlPermitted(objectJson['actor'], federationList):
        return None

    if port:
        if port != 80 and port != 443:
            if ':' not in domain:
                domain = domain + ':' + str(port)

    newAccept = {
        "@context": "https://www.w3.org/ns/activitystreams",
        'type': acceptType,
        'actor': httpPrefix+'://' + domain + '/users/' + nickname,
        'to': [toUrl],
        'cc': [],
        'object': objectJson
    }
    if ccUrl:
        if len(ccUrl) > 0:
            newAccept['cc'] = [ccUrl]
    return newAccept


def createAccept(baseDir: str, federationList: [],
                 nickname: str, domain: str, port: int,
                 toUrl: str, ccUrl: str, httpPrefix: str,
                 objectJson: {}) -> {}:
    return createAcceptReject(baseDir, federationList,
                              nickname, domain, port,
                              toUrl, ccUrl, httpPrefix,
                              objectJson, 'Accept')


def createReject(baseDir: str, federationList: [],
                 nickname: str, domain: str, port: int,
                 toUrl: str, ccUrl: str, httpPrefix: str,
                 objectJson: {}) -> {}:
    return createAcceptReject(baseDir, federationList,
                              nickname, domain, port,
                              toUrl, ccUrl,
                              httpPrefix, objectJson, 'Reject')


def acceptFollow(baseDir: str, domain: str, messageJson: {},
                 federationList: [], debug: bool) -> None:
    """Receiving a follow Accept activity
    """
    if not messageJson.get('object'):
        return
    if not messageJson['object'].get('type'):
        return
    if not messageJson['object']['type'] == 'Follow':
        return
    if debug:
        print('DEBUG: receiving Follow activity')
    if not messageJson['object'].get('actor'):
        print('DEBUG: no actor in Follow activity')
        return
    # no, this isn't a mistake
    if not messageJson['object'].get('object'):
        print('DEBUG: no object within Follow activity')
        return
    if not messageJson.get('to'):
        if debug:
            print('DEBUG: No "to" parameter in follow Accept')
        return
    if debug:
        print('DEBUG: follow Accept received')
    thisActor = messageJson['object']['actor']
    nickname = getNicknameFromActor(thisActor)
    if not nickname:
        print('WARN: no nickname found in ' + thisActor)
        return
    acceptedDomain, acceptedPort = getDomainFromActor(thisActor)
    if not acceptedDomain:
        if debug:
            print('DEBUG: domain not found in ' + thisActor)
        return
    if not nickname:
        if debug:
            print('DEBUG: nickname not found in ' + thisActor)
        return
    if acceptedPort:
        if '/' + acceptedDomain + ':' + str(acceptedPort) + \
           '/users/' + nickname not in thisActor:
            if debug:
                print('Port: ' + str(acceptedPort))
                print('Expected: /' + acceptedDomain + ':' +
                      str(acceptedPort) + '/users/' + nickname)
                print('Actual:   ' + thisActor)
                print('DEBUG: unrecognized actor ' + thisActor)
            return
    else:
        if not '/' + acceptedDomain+'/users/' + nickname in thisActor:
            if debug:
                print('Expected: /' + acceptedDomain+'/users/' + nickname)
                print('Actual:   ' + thisActor)
                print('DEBUG: unrecognized actor ' + thisActor)
            return
    followedActor = messageJson['object']['object']
    followedDomain, port = getDomainFromActor(followedActor)
    if not followedDomain:
        print('DEBUG: no domain found within Follow activity object ' +
              followedActor)
        return
    followedDomainFull = followedDomain
    if port:
        followedDomainFull = followedDomain+':' + str(port)
    followedNickname = getNicknameFromActor(followedActor)
    if not followedNickname:
        print('DEBUG: no nickname found within Follow activity object ' +
              followedActor)
        return

    acceptedDomainFull = acceptedDomain
    if acceptedPort:
        acceptedDomainFull = acceptedDomain + ':' + str(acceptedPort)

    # has this person already been unfollowed?
    unfollowedFilename = baseDir + '/accounts/' + \
        nickname + '@' + acceptedDomainFull + '/unfollowed.txt'
    if os.path.isfile(unfollowedFilename):
        if followedNickname + '@' + followedDomainFull in \
           open(unfollowedFilename).read():
            if debug:
                print('DEBUG: follow accept arrived for ' +
                      nickname + '@' + acceptedDomainFull +
                      ' from ' + followedNickname + '@' + followedDomainFull +
                      ' but they have been unfollowed')
            return

    if followPerson(baseDir,
                    nickname, acceptedDomainFull,
                    followedNickname, followedDomainFull,
                    federationList, debug):
        if debug:
            print('DEBUG: ' + nickname + '@' + acceptedDomainFull +
                  ' followed ' + followedNickname + '@' + followedDomainFull)
    else:
        if debug:
            print('DEBUG: Unable to create follow - ' +
                  nickname + '@' + acceptedDomain+' -> ' +
                  followedNickname + '@' + followedDomain)


def receiveAcceptReject(session, baseDir: str,
                        httpPrefix: str, domain: str, port: int,
                        sendThreads: [], postLog: [], cachedWebfingers: {},
                        personCache: {}, messageJson: {}, federationList: [],
                        debug: bool) -> bool:
    """Receives an Accept or Reject within the POST section of HTTPServer
    """
    if messageJson['type'] != 'Accept' and messageJson['type'] != 'Reject':
        return False
    if not messageJson.get('actor'):
        if debug:
            print('DEBUG: ' + messageJson['type'] + ' has no actor')
        return False
    if '/users/' not in messageJson['actor'] and \
       '/accounts/' not in messageJson['actor'] and \
       '/channel/' not in messageJson['actor'] and \
       '/profile/' not in messageJson['actor']:
        if debug:
            print('DEBUG: "users" or "profile" missing from actor in ' +
                  messageJson['type'] + '. Assuming single user instance.')
    domain, tempPort = getDomainFromActor(messageJson['actor'])
    if not domainPermitted(domain, federationList):
        if debug:
            print('DEBUG: ' + messageJson['type'] +
                  ' from domain not permitted - ' + domain)
        return False
    nickname = getNicknameFromActor(messageJson['actor'])
    if not nickname:
        # single user instance
        nickname = 'dev'
        if debug:
            print('DEBUG: ' + messageJson['type'] +
                  ' does not contain a nickname. ' +
                  'Assuming single user instance.')
    # receive follow accept
    acceptFollow(baseDir, domain, messageJson, federationList, debug)
    if debug:
        print('DEBUG: Uh, ' + messageJson['type'] + ', I guess')
    return True