import json
from typing import Optional

from accounts.models import (
    AccountModel,
    ActionModel,
    BusinessModel,
    UserModel,
    WorkspaceModel,
)
from api.serializers import BaseSerializer, ListItemsWithURLSerializer
from api.serializers.mixins import ActionMixinSerializer
from common.utils import ModelManager
from django.core.exceptions import ObjectDoesNotExist
from rest_framework import serializers

from scalade.entities import ActionEntity


class WorkspaceCreationSerializer(BaseSerializer):
    name = serializers.CharField(max_length=50)
    business = serializers.UUIDField()

    def create(self, validated_data):
        workspace = ModelManager.handle(
            "accounts.workspace",
            "create",
            name=validated_data["name"],
            business=validated_data["business"],
        )
        return workspace

    def validate_name(self, name):
        query = ModelManager.handle(
            "accounts.workspace",
            "filter",
            name=name,
            business__uuid=self.initial_data.get("business"),
        )
        if len(query) > 0:
            raise serializers.ValidationError(
                "A workspace with that name '%s' already exists "
                "at the scope of the business." % name
            )
        return name

    def validate_business(self, business_uuid):
        try:
            business = ModelManager.handle(
                "accounts.business", "get", uuid=business_uuid
            )
        except ObjectDoesNotExist:
            raise serializers.ValidationError(
                "Business is invalid: '%s' doesn't exist." % business_uuid
            )
        return business


class WorkspaceSerializer(serializers.ModelSerializer):
    class Meta:
        model = WorkspaceModel
        fields = "__all__"


class WorkspaceListSerializer(ListItemsWithURLSerializer):
    url_basename = "entities-api:workspaces-detail"
    url = serializers.SerializerMethodField()

    class Meta:
        model = WorkspaceModel
        fields = ["uuid", "name", "business", "url"]


class AccountSerializer(serializers.ModelSerializer):
    groups = serializers.SerializerMethodField()

    class Meta:
        model = AccountModel
        exclude = ["password"]

    def get_groups(self, obj) -> list[str]:
        return [grp.name for grp in obj.groups.all()]


class AccountListSerializer(ListItemsWithURLSerializer):
    url_basename = "entities-api:accounts-detail"
    url = serializers.SerializerMethodField()

    class Meta:
        model = AccountModel
        fields = ["uuid", "created", "auth_id", "username", "email", "url"]


class BusinessSerializer(serializers.ModelSerializer):
    class Meta:
        model = BusinessModel
        fields = "__all__"


class BusinessExtentedSerializer(serializers.ModelSerializer):
    username = serializers.SerializerMethodField()
    email = serializers.SerializerMethodField()

    class Meta:
        model = BusinessModel
        fields = "__all__"

    def get_username(self, obj):
        return obj.master_account.username

    def get_email(self, obj):
        return obj.master_account.email


class BusinessListSerializer(ListItemsWithURLSerializer):
    url_basename = "entities-api:businesses-detail"
    url = serializers.SerializerMethodField()

    class Meta:
        model = BusinessModel
        fields = ["uuid", "organization_name", "url"]


class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = UserModel
        fields = "__all__"


class UserExtentedSerializer(serializers.ModelSerializer):
    username = serializers.SerializerMethodField()
    email = serializers.SerializerMethodField()
    organization_slug = serializers.SerializerMethodField()

    class Meta:
        model = UserModel
        fields = "__all__"

    def get_username(self, obj):
        return obj.account.username

    def get_email(self, obj):
        return obj.account.email

    def get_organization_slug(self, obj):
        return obj.business.organization_slug


class UserListSerializer(ListItemsWithURLSerializer):
    url_basename = "entities-api:users-detail"
    url = serializers.SerializerMethodField()

    class Meta:
        model = UserModel
        fields = ["uuid", "first_name", "url"]


class ActionCreationSerializer(BaseSerializer):
    kind = serializers.CharField(max_length=50)
    level = serializers.CharField(max_length=50)
    message = serializers.CharField(max_length=150)
    extra_fields = serializers.JSONField(
        encoder=json.JSONEncoder, required=False, allow_null=True
    )

    def create(self, validated_data):
        return ModelManager.handle(
            "accounts.action", "create", **validated_data
        )

    def validate_kind(self, kind: str):
        kind_values = [kv[0] for kv in ActionEntity.KIND_VALUES]
        if kind not in kind_values:
            raise serializers.ValidationError(
                "Not a valid action kind; valid kinds are %s" % kind_values
            )
        return kind

    def validate_level(self, level: str):
        level_values = [lv[0] for lv in ActionEntity.LEVEL_VALUES]
        if level not in level_values:
            raise serializers.ValidationError(
                "Not a valid action level; valid levels are %s" % level_values
            )
        return level

    def validate_extra_fields(self, extra_fields: Optional[dict] = None):
        if extra_fields is None:
            return None
        try:
            assert isinstance(extra_fields, dict)
            ser_extra_fields = json.dumps(extra_fields)
            assert json.loads(ser_extra_fields) == extra_fields
        except AssertionError:
            raise serializers.ValidationError(
                "'extra_fields' value is not valid Object."
            )
        return ser_extra_fields


class ActionSerializer(ActionMixinSerializer):
    class Meta:
        model = ActionModel
        fields = "__all__"


class ActionListSerializer(ActionMixinSerializer, ListItemsWithURLSerializer):
    url_basename = "entities-api:actions-detail"
    url = serializers.SerializerMethodField()

    class Meta:
        model = ActionModel
        fields = ["uuid", "created", "kind", "level", "message", "url"]

import json
from typing import Optional

from accounts.models import (
    AccountModel,
    ActionModel,
    BusinessModel,
    UserModel,
    WorkspaceModel,
)
from api.serializers import BaseSerializer, ListItemsWithURLSerializer
from api.serializers.mixins import ActionMixinSerializer
from common.utils import ModelManager
from django.core.exceptions import ObjectDoesNotExist
from rest_framework import serializers

from scalade.entities import ActionEntity


class WorkspaceCreationSerializer(BaseSerializer):
    name = serializers.CharField(max_length=50)
    business = serializers.UUIDField()

    def create(self, validated_data):
        workspace = ModelManager.handle(
            "accounts.workspace",
            "create",
            name=validated_data["name"],
            business=validated_data["business"],
        )
        return workspace

    def validate_name(self, name):
        query = ModelManager.handle(
            "accounts.workspace",
            "filter",
            name=name,
            business__uuid=self.initial_data.get("business"),
        )
        if len(query) > 0:
            raise serializers.ValidationError(
                "A workspace with that name '%s' already exists "
                "at the scope of the business." % name
            )
        return name

    def validate_business(self, business_uuid):
        try:
            business = ModelManager.handle(
                "accounts.business", "get", uuid=business_uuid
            )
        except ObjectDoesNotExist:
            raise serializers.ValidationError(
                "Business is invalid: '%s' doesn't exist." % business_uuid
            )
        return business


class WorkspaceSerializer(serializers.ModelSerializer):
    class Meta:
        model = WorkspaceModel
        fields = "__all__"


class WorkspaceListSerializer(ListItemsWithURLSerializer):
    url_basename = "entities-api:workspaces-detail"
    url = serializers.SerializerMethodField()

    class Meta:
        model = WorkspaceModel
        fields = ["uuid", "name", "business", "url"]


class AccountSerializer(serializers.ModelSerializer):
    groups = serializers.SerializerMethodField()

    class Meta:
        model = AccountModel
        exclude = ["password"]

    def get_groups(self, obj) -> list[str]:
        return [grp.name for grp in obj.groups.all()]


class AccountListSerializer(ListItemsWithURLSerializer):
    url_basename = "entities-api:accounts-detail"
    url = serializers.SerializerMethodField()

    class Meta:
        model = AccountModel
        fields = ["uuid", "created", "auth_id", "username", "email", "url"]


class BusinessSerializer(serializers.ModelSerializer):
    class Meta:
        model = BusinessModel
        fields = "__all__"


class BusinessExtentedSerializer(serializers.ModelSerializer):
    username = serializers.SerializerMethodField()
    email = serializers.SerializerMethodField()

    class Meta:
        model = BusinessModel
        fields = "__all__"

    def get_username(self, obj):
        return obj.master_account.username

    def get_email(self, obj):
        return obj.master_account.email


class BusinessListSerializer(ListItemsWithURLSerializer):
    url_basename = "entities-api:businesses-detail"
    url = serializers.SerializerMethodField()

    class Meta:
        model = BusinessModel
        fields = ["uuid", "organization_name", "url"]


class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = UserModel
        fields = "__all__"


class UserExtentedSerializer(serializers.ModelSerializer):
    username = serializers.SerializerMethodField()
    email = serializers.SerializerMethodField()
    organization_slug = serializers.SerializerMethodField()

    class Meta:
        model = UserModel
        fields = "__all__"

    def get_username(self, obj):
        return obj.account.username

    def get_email(self, obj):
        return obj.account.email

    def get_organization_slug(self, obj):
        return obj.business.organization_slug


class UserListSerializer(ListItemsWithURLSerializer):
    url_basename = "entities-api:users-detail"
    url = serializers.SerializerMethodField()

    class Meta:
        model = UserModel
        fields = ["uuid", "first_name", "url"]


class ActionCreationSerializer(BaseSerializer):
    kind = serializers.CharField(max_length=50)
    level = serializers.CharField(max_length=50)
    message = serializers.CharField(max_length=150)
    extra_fields = serializers.JSONField(
        encoder=json.JSONEncoder, required=False, allow_null=True
    )

    def create(self, validated_data):
        return ModelManager.handle(
            "accounts.action", "create", **validated_data
        )

    def validate_kind(self, kind: str):
        kind_values = [kv[0] for kv in ActionEntity.KIND_VALUES]
        if kind not in kind_values:
            raise serializers.ValidationError(
                "Not a valid action kind; valid kinds are %s" % kind_values
            )
        return kind

    def validate_level(self, level: str):
        level_values = [lv[0] for lv in ActionEntity.LEVEL_VALUES]
        if level not in level_values:
            raise serializers.ValidationError(
                "Not a valid action level; valid levels are %s" % level_values
            )
        return level

    def validate_extra_fields(self, extra_fields: Optional[dict] = None):
        if extra_fields is None:
            return None
        try:
            assert isinstance(extra_fields, dict)
            ser_extra_fields = json.dumps(extra_fields)
            assert json.loads(ser_extra_fields) == extra_fields
        except AssertionError:
            raise serializers.ValidationError(
                "'extra_fields' value is not valid Object."
            )
        return ser_extra_fields


class ActionSerializer(ActionMixinSerializer):
    class Meta:
        model = ActionModel
        fields = "__all__"


class ActionListSerializer(ActionMixinSerializer, ListItemsWithURLSerializer):
    url_basename = "entities-api:actions-detail"
    url = serializers.SerializerMethodField()

    class Meta:
        model = ActionModel
        fields = ["uuid", "created", "kind", "level", "message", "url"]
