from datetime import datetime
from logging import Logger

import attr

from apiserver.database.model.auth import Role
from apiserver.database.model.auth import User as AuthUser, Credentials
from apiserver.database.model.user import User
from apiserver.service_repo.auth.fixed_user import FixedUser


def _ensure_user_credentials(
    user: AuthUser,
    key: str,
    secret: str,
    log: Logger,
    revoke: bool = False,
    internal_user: bool = False,
) -> None:
    if revoke:
        log.info(f"Revoking credentials for existing user {user.id} ({user.name})")
        user.credentials = []
        user.save()
        return

    if not (key and secret):
        if internal_user:
            log.info(f"Resetting credentials for existing user {user.id} ({user.name})")
            user.credentials = []
            user.save()
        return

    new_credentials = Credentials(key=key, secret=secret)
    if internal_user:
        log.info(f"Setting credentials for existing user {user.id} ({user.name})")
        user.credentials = [new_credentials]
        user.save()
        return

    if user.credentials is None:
        user.credentials = []
    if not any((cred.key, cred.secret) == (key, secret) for cred in user.credentials):
        log.info(f"Adding credentials for existing user {user.id} ({user.name})")
        user.credentials.append(new_credentials)
        user.save()


def _ensure_auth_user(
    user_data: dict,
    company_id: str,
    log: Logger,
    revoke: bool = False,
    internal_user: bool = False,
) -> str:
    user_id = user_data.get("id", f"__{user_data['name']}__")
    role = user_data["role"]
    email = user_data["email"]
    autocreated = user_data.get("autocreated", False)
    key, secret = user_data.get("key"), user_data.get("secret")

    user: AuthUser = AuthUser.objects(id=user_id).first()
    if user:
        _ensure_user_credentials(
            user=user,
            key=key,
            secret=secret,
            log=log,
            revoke=revoke,
            internal_user=internal_user,
        )
        if user.role != role or user.email != email or user.autocreated != autocreated:
            user.email = email
            user.role = role
            user.autocreated = autocreated
            user.save()

        return user.id

    credentials = (
        [Credentials(key=key, secret=secret)] if not revoke and key and secret else []
    )
    log.info(f"Creating user: {user_data['name']}")

    user = AuthUser(
        id=user_id,
        name=user_data["name"],
        company=company_id,
        role=role,
        email=email,
        created=datetime.utcnow(),
        credentials=credentials,
        autocreated=autocreated,
    )

    user.save()

    return user.id


def _ensure_backend_user(user_id: str, company_id: str, user_name: str):
    given_name, _, family_name = user_name.partition(" ")

    User(
        id=user_id,
        company=company_id,
        name=user_name,
        given_name=given_name,
        family_name=family_name,
        created=datetime.utcnow(),
    ).save()

    return user_id


def ensure_fixed_user(user: FixedUser, log: Logger, emails: set):
    # noinspection PyTypeChecker
    data = attr.asdict(user)
    data["id"] = user.user_id
    email = f"{user.user_id}@example.com"
    data["email"] = email
    data["role"] = Role.guest if user.is_guest else Role.user
    data["autocreated"] = True

    _ensure_auth_user(user_data=data, company_id=user.company, log=log)

    db_user = User.objects(company=user.company, id=user.user_id).first()
    if db_user:
        # noinspection PyBroadException
        try:
            log.info(f"Updating user name: {user.name}")
            given_name, _, family_name = user.name.partition(" ")
            db_user.update(
                name=user.name, given_name=given_name, family_name=family_name
            )
        except Exception:
            pass
    else:
        _ensure_backend_user(user.user_id, user.company, user.name)

    emails.add(email)