import json
import requests
import base64, hashlib, hmac, time
import email.utils
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import padding
from urllib.parse import urlparse
from db.db import async_session
from models.users import RemoteUser
from sqlmodel import select
from activitypub.activitystreams import ActivityTypes


async def send_activity(private_key: str, user_id: str, target_id: str, body: dict):
    # 1. Fetch target inbox
    # 1.1 Check if we know the user's inbox
    async with async_session() as s:
        statement = select(RemoteUser).where(RemoteUser.remote_id == target_id)
        results = await s.execute(statement)
        user = results.first()
    
    if user:
        inbox = user[0].inbox
    else:
        # 1.2 If not, fetch from remote server
        resp = requests.get(target_id, headers={'accept': 'application/activity+json'})
        target = resp.json()
        inbox = target['inbox']

        remote_user = RemoteUser(remote_id=target_id, inbox=target['inbox'], public_key=target['publicKey']['publicKeyPem'])
        async with async_session() as s:
            s.add(remote_user)
            await s.commit()
    
    # 2. Send activity to target inbox
    private_key = serialization.load_pem_private_key(private_key.encode('utf-8'), password=None)
    urlparts = urlparse(target_id)

    m = hashlib.sha256()
    m.update(json.dumps(body).encode('utf-8'))
    digestHash = base64.b64encode(m.digest()).decode()

    created_timestamp = int(time.time())
    header_time = email.utils.formatdate(created_timestamp, usegmt=True)

    toSign = '(request-target): post {}\nhost: {}\ndate: {}\ndigest: SHA-256={}'.format(
        urlparts.path,
        urlparts.netloc,
        header_time,
        digestHash
    )

    signed = private_key.sign(
        toSign.encode('utf-8'),
        padding.PKCS1v15(),
        hashes.SHA256()
    )

    signature = 'keyId="{}#main-key",headers="(request-target) host date digest",signature="{}"'.format(
        user_id,
        base64.b64encode(signed).decode(),
    )
    headers = {
        'Content-type': 'application/ld+json',
        'Host': urlparts.netloc,
        'Date': header_time,
        'Signature': signature,
        'Digest': 'SHA-256={}'.format(digestHash)
    }
    resp = requests.post(inbox, json=body, headers=headers)
    print(resp.text)
models.users.RemoteUser db.db.async_session cryptography.hazmat.primitives.hashes.SHA256 sqlmodel.select json.dumps time.time hashlib.sha256 base64.b64encode requests.get requests.post urllib.parse.urlparse cryptography.hazmat.primitives.asymmetric.padding.PKCS1v15
[((1382, 1401), 'urllib.parse.urlparse', 'urlparse', (['target_id'], {}), '(target_id)\n', (1390, 1401), False, 'from urllib.parse import urlparse\n'), ((1411, 1427), 'hashlib.sha256', 'hashlib.sha256', ([], {}), '()\n', (1425, 1427), False, 'import base64, hashlib, hmac, time\n'), ((2347, 2395), 'requests.post', 'requests.post', (['inbox'], {'json': 'body', 'headers': 'headers'}), '(inbox, json=body, headers=headers)\n', (2360, 2395), False, 'import requests\n'), ((567, 582), 'db.db.async_session', 'async_session', ([], {}), '()\n', (580, 582), False, 'from db.db import async_session\n'), ((865, 937), 'requests.get', 'requests.get', (['target_id'], {'headers': "{'accept': 'application/activity+json'}"}), "(target_id, headers={'accept': 'application/activity+json'})\n", (877, 937), False, 'import requests\n'), ((1022, 1129), 'models.users.RemoteUser', 'RemoteUser', ([], {'remote_id': 'target_id', 'inbox': "target['inbox']", 'public_key': "target['publicKey']['publicKeyPem']"}), "(remote_id=target_id, inbox=target['inbox'], public_key=target[\n    'publicKey']['publicKeyPem'])\n", (1032, 1129), False, 'from models.users import RemoteUser\n'), ((1559, 1570), 'time.time', 'time.time', ([], {}), '()\n', (1568, 1570), False, 'import base64, hashlib, hmac, time\n'), ((1901, 1919), 'cryptography.hazmat.primitives.asymmetric.padding.PKCS1v15', 'padding.PKCS1v15', ([], {}), '()\n', (1917, 1919), False, 'from cryptography.hazmat.primitives.asymmetric import padding\n'), ((1929, 1944), 'cryptography.hazmat.primitives.hashes.SHA256', 'hashes.SHA256', ([], {}), '()\n', (1942, 1944), False, 'from cryptography.hazmat.primitives import hashes, serialization\n'), ((1144, 1159), 'db.db.async_session', 'async_session', ([], {}), '()\n', (1157, 1159), False, 'from db.db import async_session\n'), ((609, 627), 'sqlmodel.select', 'select', (['RemoteUser'], {}), '(RemoteUser)\n', (615, 627), False, 'from sqlmodel import select\n'), ((1441, 1457), 'json.dumps', 'json.dumps', (['body'], {}), '(body)\n', (1451, 1457), False, 'import json\n'), ((2082, 2106), 'base64.b64encode', 'base64.b64encode', (['signed'], {}), '(signed)\n', (2098, 2106), False, 'import base64, hashlib, hmac, time\n')]