import enum

import nanoid
from nanoid_dictionary import human_alphabet
from slugify import slugify
from sqlalchemy import Column, String, inspect
from sqlalchemy.orm import DeclarativeBase, MappedAsDataclass


def genslug():
    return nanoid.generate(human_alphabet[2:], 5)


class SlugMixin:
    slug = Column(String, index=True, unique=True, default=genslug)

    @property
    def uri(self):
        return "-".join([self.slug, slugify(self.name.title().replace(" ", ""), ok="", only_ascii=True, lower=False)])


class BaseObject(MappedAsDataclass, DeclarativeBase):
    """
    Allows for iterating over Model objects' column names and values
    """

    __abstract__ = True

    def __iter__(self):
        values = vars(self)
        for attr in self.__mapper__.columns.keys():
            if attr in values:
                yield attr, values[attr]
        for relname in self.__mapper__.relationships.keys():
            relvals = []
            reliter = self.__getattribute__(relname)
            if not reliter:
                yield relname, relvals
                continue
            for rel in reliter:
                try:
                    relvals.append({k: v for k, v in vars(rel).items() if not k.startswith("_")})
                except TypeError:
                    relvals.append(rel)
            yield relname, relvals

    def __json__(self):
        """
        Provide a custom JSON encoder.
        """
        raise NotImplementedError()

    def __repr__(self):
        return str(dict(self))

    def copy(self):
        self_as_dict = dict(self.__dict__)
        self_as_dict.pop("_sa_instance_state")
        mapper = inspect(self).mapper
        for primary_key in mapper.primary_key:
            self_as_dict.pop(primary_key.name)
        for key in mapper.relationships.keys():
            if key in self_as_dict:
                self_as_dict.pop(key)
        return self.__class__(**self_as_dict)


class EnumField(enum.Enum):
    """
    A serializable enum.
    """

    def __json__(self):
        return self.value


STATS = ["strength", "dexterity", "constitution", "intelligence", "wisdom", "charisma"]

CREATURE_TYPES = [
    "aberation",
    "beast",
    "celestial",
    "construct",
    "dragon",
    "elemental",
    "fey",
    "fiend",
    "Giant",
    "humanoid",
    "monstrosity",
    "ooze",
    "plant",
    "undead",
]
SIZES = ["Tiny", "Small", "Medium", "Large", "Huge", "Gargantuan"]

CreatureTypesEnum = EnumField("CreatureTypesEnum", ((k, k) for k in CREATURE_TYPES))
StatsEnum = EnumField("StatsEnum", ((k, k) for k in STATS))
SizesEnum = EnumField("SizesEnum", ((k, k) for k in SIZES))
