from typing import Any, Dict, List, Optional

import attr
from marshmallow import ValidationError, pre_load, validates_schema
from marshmallow_annotations.ext.attrs import AttrsSchema

"""
TODO: Explore all internationalization use cases and
redesign how User handles names

TODO - Delete pre processing of the Data
Once all of the upstream services provide a complete User object we will no
longer need to supplement the User objects as done in `preprocess_data`
"""


@attr.s(auto_attribs=True, kw_only=True)
class User:
    user_id: Optional[str] = None
    email: Optional[str] = None
    first_name: Optional[str] = None
    last_name: Optional[str] = None
    full_name: Optional[str] = None
    display_name: Optional[str] = None
    is_active: bool = True
    github_username: Optional[str] = None
    team_name: Optional[str] = None
    slack_id: Optional[str] = None
    employee_type: Optional[str] = None
    manager_fullname: Optional[str] = None
    manager_email: Optional[str] = None
    manager_id: Optional[str] = None
    role_name: Optional[str] = None
    profile_url: Optional[str] = None
    profile_picture: Optional[str] = None
    background_picture: Optional[str] = None
    role: Optional[str] = None
    dismissed: Optional[List[str]] = None
    has_environment: Optional[bool] = None
    service_account: Optional[bool] = None
    other_key_values: Optional[Dict[str, str]] = attr.ib(factory=dict)  # type: ignore


class UserSchema(AttrsSchema):
    class Meta:
        target = User
        register_as_scheme = True

    # noinspection PyMethodMayBeStatic
    def _str_no_value(self, s: Optional[str]) -> bool:
        # Returns True if the given string is None or empty
        if not s:
            return True
        if len(s.strip()) == 0:
            return True
        return False

    @pre_load
    def preprocess_data(self, data: Dict[str, Any]) -> Dict[str, Any]:
        if self._str_no_value(data.get("user_id")):
            data["user_id"] = data.get("email")

        if self._str_no_value(data.get("profile_url")):
            data["profile_url"] = ""
            if data.get("GET_PROFILE_URL"):
                data["profile_url"] = data.get("GET_PROFILE_URL")(data["user_id"])  # type: ignore

        first_name = data.get("first_name")
        last_name = data.get("last_name")

        if self._str_no_value(data.get("full_name")) and first_name and last_name:
            data["full_name"] = f"{first_name} {last_name}"

        if self._str_no_value(data.get("display_name")):
            if self._str_no_value(data.get("full_name")):
                data["display_name"] = data.get("email")
            else:
                data["display_name"] = data.get("full_name")

        return data

    @validates_schema
    def validate_user(self, data: Dict[str, Any]) -> None:
        if self._str_no_value(data.get("display_name")):
            raise ValidationError(
                '"display_name", "full_name", or "email" must be provided'
            )

        if self._str_no_value(data.get("user_id")):
            raise ValidationError('"user_id" or "email" must be provided')
