import base64
from hashlib import pbkdf2_hmac
from typing import Any, Optional

from cohost.models.notification import buildFromNotifList
from cohost.models.project import EditableProject
from cohost.network import fetch, fetchTrpc, generate_login_cookies


class User:
    def __init__(self, cookie):
        self.cookie = cookie

    def __str__(self):
        return "user_{}".format(self.userId)

    @property
    def email(self):
        return self.userInfo['email']

    @property
    def userId(self):
        return self.userInfo['userId']

    @property
    def modMode(self):
        return self.userInfo['modMode']

    @property
    def activated(self):
        return self.userInfo['activated']

    @property
    def readOnly(self):
        return self.userInfo['readOnly']

    @property
    def defaultProject(self):
        from cohost.models.project import EditableProject
        return EditableProject(self, self.userInfo['projectId'])

    @property
    def userInfo(self):
        return fetchTrpc('login.loggedIn', self.cookie)['result']['data']

    """Fetch data from the API about projets the user can edit

    Returns:
        dict: Project dictionaries
    """
    @property
    def editedProjectsRaw(self):
        rawResp = fetchTrpc('projects.listEditedProjects', self.cookie)
        return rawResp['result']['data']['projects']

    """Fetch data from the API about projects the user can edit

    Returns:
        list[Project]: Project objects
    """
    @property
    def editedProjects(self):
        rawP = self.editedProjectsRaw
        projects = []
        for project in rawP:
            projects.append(EditableProject(self, project['projectId']))
        return projects

    """Get a salt for an email - for use in logging in


    Returns:
        str: Base64 encoded salt
    """
    @staticmethod
    def getSalt(email):

        salt = fetch(
            "GET",
            "/login/salt",
            {'email': email}
        )
        return salt

    """Create a user object from a login and password"""
    @staticmethod
    def login(email, password):
        # base64 terribleness
        salt = fetch("GET", "/login/salt", {"email": email})['salt']
        # explanation for whatever this is by @iliana
        # https://cohost.org/iliana/post/180187-eggbug-rs-v0-1-3-d
        salt = salt.replace('-', 'A')
        salt = salt.replace('_', 'A')
        salt = salt + "=="

        saltDecoded = base64.b64decode(salt.encode("ascii"))

        # generating the hash
        # the 200000 is the magic iter number - Use This, or login will break
        hash = pbkdf2_hmac("sha384", password.encode("utf-8"), saltDecoded,
                           200000, 128)
        clientHash = base64.b64encode(hash).decode("ascii")

        # getting cookie
        res = fetch("POST", "/login",
                    {"email": email, "clientHash": clientHash}, complex=True)
        sessionCookie = (res['headers']['set-cookie'].
                         split(";")[0].split("=")[1])

        u = User(sessionCookie)
        # if no error we're good
        u.userInfo
        return u

    """Create a user object from a cookie"""
    @staticmethod
    def loginWithCookie(cookie):
        # First, let's create a user
        u = User(cookie)
        # Now, we need to validate functions are working
        # We can do this by getting the user's info
        u.userInfo
        # If this didn't error out, we're good!
        return u

    def getProject(self, handle):
        # Retrieve a project that you can edit
        projects = self.editedProjects
        for project in projects:
            if project.handle == handle:
                return project
        return None

    def resolveSecondaryProject(self, projectData):
        from cohost.models.project import Project
        editableProjects = self.editedProjects
        for project in editableProjects:
            if project.projectId == projectData['projectId']:
                return project
        return Project(self, projectData)

    @property
    def notifications(self):
        return self.notificationsPagified(notificationsPerPage=10)

    @property
    def allNotifications(self):
        page = 0
        notifsPerPage = 100
        notifs = self.notificationsPagified(
            page=page,
            notificationsPerPage=notifsPerPage
        )
        newNotifs = notifs
        while len(newNotifs) == notifsPerPage:
            page += 1
            newNotifs = self.notificationsPagified(
                page=page,
                notificationsPerPage=notifsPerPage
            )
            for n in newNotifs:
                notifs.append(n)
        return notifs

    def notificationsPagified(self, page=0, notificationsPerPage=10):
        nJson = self.notificationsRaw(page, notificationsPerPage)
        return buildFromNotifList(nJson, self)

    def notificationsRaw(self, page=0, notificationsPerPage=10):
        return fetch('GET', 'notifications/list', {
            'offset': page * notificationsPerPage,
            'limit': notificationsPerPage
        }, generate_login_cookies(self.cookie))