"""
Mock classes to test NotaBLE API from a simple environment.

NotaBLE és la col·laboració entre Gwido i el Workspace educatiu DD.

És un projecte de Xnet, IsardVDI, Gwido i Taller de Músics,
guanyador de la Ciutat Proactiva 2021, suport a la innovació urbana de
la Fundació BitHabitat.
"""
import copy
from typing import Any, Dict, Iterable, List, Optional, cast

from admin.flaskapp import AdminFlaskApp
from admin.lib.admin import Admin
from admin.lib.keycloak_client import KeycloakClient


class MockKeycloakAdmin:
    app: AdminFlaskApp

    def __init__(self, app: AdminFlaskApp) -> None:
        self.app = app

    def _convert_kc(self, data: Dict[str, Any]) -> Dict[str, Any]:
        u = {
            "email": data["email"],
            "id": data["id"],
            "username": data["username"],
            "first": data["firstName"],
            "last": data["lastName"],
            "enabled": data["enabled"],
            "roles": [],
        }
        return u

    def create_user(self, data: Dict[str, Any]) -> Any:
        uid = data.get("username")
        us = {u["id"]: u for u in self.app.admin.internal["users"]}
        if not uid or uid in us:
            raise Exception("Invalid or existing user")
        u = copy.deepcopy(data)
        u["id"] = uid
        u = self._convert_kc(u)
        self.app.admin.internal["users"].append(u)
        return uid

    def group_user_add(self, user_id: str, group_id: str) -> Any:
        o = {u["id"]: u for u in self.app.admin.internal["users"]}.get(user_id, {})
        o["groups"] = list(set(o.get("groups", []) + [group_id]))
        return o

    def delete_user(self, user_id: str) -> None:
        self.app.admin.internal["users"] = [
            u for u in self.app.admin.internal["users"] if u["id"] != user_id
        ]

    def get_groups(self) -> List[Dict[str, Any]]:
        return cast(List[Dict[str, Any]], self.app.admin.internal["groups"])

    def get_realm_roles_of_user(self, user_id: str) -> List[Dict[str, Any]]:
        o = {u["id"]: u for u in self.app.admin.internal["users"]}.get(user_id, {})
        return [{"name": r} for r in o.get("roles", [])]

    def get_group(self, group_id: str) -> Dict[str, Any]:
        o = {g["id"]: g for g in self.app.admin.internal["groups"]}.get(group_id, {})
        return cast(Dict[str, Any], o)

    def create_group(self, group: Dict[str, Any], parent: Optional[str] = None) -> Any:
        g = copy.deepcopy(group)
        g.update(
            {
                "id": g["name"],
                "path": "/".join(["", parent, g["name"]]) if parent else "",
            }
        )
        self.app.admin.internal["groups"].append(g)

    def delete_user_realm_role(self, user_id: str, roles: str) -> None:
        o = {u["id"]: u for u in self.app.admin.internal["users"]}.get(user_id, {})
        o["roles"] = [r for r in o.get("roles", []) if r not in roles]
        o["keycloak_groups"] = o["roles"]

    def update_user(self, user_id: str, payload: Dict[str, Any]) -> Any:
        pass


class MockKeycloak(KeycloakClient):
    app: AdminFlaskApp

    def __init__(self, app: AdminFlaskApp) -> None:
        self.app = app
        self.keycloak_admin = MockKeycloakAdmin(app)

    def get_group_by_path(self, path: str, recursive: bool = True) -> Any:
        gs = {g["id"]: g for g in self.app.admin.internal["groups"]}
        return gs.get(path.lstrip("/"))

    def assign_realm_roles(self, uid: str, role: str) -> Dict[str, Any]:
        o: Dict[str, Any] = {u["id"]: u for u in self.app.admin.internal["users"]}.get(
            uid, {}
        )
        o["roles"] = list(set(o.get("roles", []) + [role]))
        o["keycloak_groups"] = o["roles"]
        return o

    def connect(self) -> None:
        pass

    def user_update(
        self,
        user_id: str,
        enabled: bool,
        email: str,
        first: str,
        last: str,
        groups: Iterable[str] = [],
        roles: Iterable[str] = [],
    ) -> Dict[str, Any]:
        o: Dict[str, Any] = {u["id"]: u for u in self.app.admin.internal["users"]}.get(
            user_id, {}
        )
        o.update({"first": first, "last": last, "enabled": enabled})
        o["groups"] = list(sorted(o["groups"]))
        return o

    def delete_group(self, group_id: str) -> None:
        self.app.admin.internal["groups"] = [
            g for g in self.app.admin.internal["groups"] if g["id"] != group_id
        ]


class MockMoodle:
    app: AdminFlaskApp

    def __init__(self, app: AdminFlaskApp):
        self.app = app

    def create_user(
        self,
        email: str,
        username: str,
        password: str,
        first_name: str = "-",
        last_name: str = "-",
    ) -> List[Dict[str, Any]]:
        o = {u["id"]: u for u in self.app.admin.internal["users"]}.get(username, {})
        o["moodle"] = True
        o["moodle_id"] = username
        return [{"id": username, "username": username}]

    def get_cohorts(self) -> List[Dict[str, Any]]:
        return cast(List[Dict[str, Any]], self.app.admin.internal["groups"])

    def add_user_to_cohort(self, userid: str, cohortid: str) -> List[Dict[str, Any]]:
        return [{"id": userid, "username": userid}]

    def delete_users(self, user_id: str) -> None:
        for u in user_id:
            self.app.admin.delete_user(user_id)

    def update_user(
        self,
        username: str,
        email: str,
        first_name: str,
        last_name: str,
        enabled: bool = True,
    ) -> None:
        pass

    def add_system_cohort(
        self, name: str, description: str, visible: bool = True
    ) -> Dict[str, Any]:
        return {"name": name}

    def delete_cohorts(self, group_id: str) -> None:
        pass


class MockNextcloud:
    app: AdminFlaskApp

    def __init__(self, app: AdminFlaskApp):
        self.app = app

    # nextcloud
    def add_user_with_groups(
        self,
        userid: str,
        userpassword: str,
        quota: Any = False,
        groups: Any = [],
        email: str = "",
        displayname: str = "",
    ) -> bool:
        o = {u["id"]: u for u in self.app.admin.internal["users"]}.get(userid, {})
        o["quota"] = quota
        o["quota_used_bytes"] = 0
        return True

    def delete_user(self, userid: str) -> None:
        pass

    def update_user(self, user_id: str, user: Dict[str, Any]) -> Any:
        pass

    def add_user_to_group(self, user_id: str, group_id: str) -> bool:
        return True

    def add_group(self, groupid: str) -> bool:
        return True

    def delete_group(self, group_id: str) -> None:
        pass


class MockAdmin(Admin):
    """
    Mock class to easily test the API views.
    """

    app: AdminFlaskApp
    internal: Dict[str, Any]

    def __init__(self, app: AdminFlaskApp):
        """
        Constructor override so we can use the class for testing
        """
        self.app = app
        self.av = self  # type: ignore
        self.moodle = MockMoodle(app)  # type: ignore
        self.nextcloud = MockNextcloud(app)  # type: ignore
        self.third_party_cbs = []
        # Initialise data
        self.internal = {
            "users": [],
            "groups": [],
            "roles": [],
        }

    def resync_data(self) -> bool:
        return True

    # avatars
    def add_user_default_avatar(self, uid: str, role: str) -> None:
        pass

    def delete_user_avatar(self, uid: str) -> None:
        pass
