from dataclasses import _process_class, dataclass, field, asdict
from enum import IntEnum
from typing import Any

from edri.dataclass.directive import ResponseDirective
from edri.utility.function import snake2camel


class ResponseStatus(IntEnum):
    NONE = -1
    OK = 0
    FAILED = 1


@dataclass
class Response:
    _status: ResponseStatus = field(init=False, default=ResponseStatus.NONE)
    _changed: bool | None = field(init=False, default=None)
    _directives: list[ResponseDirective] = field(init=False, default_factory=list)

    def as_dict(self, /, *, transform: bool, keep_concealed: bool) -> dict[str, Any]:
        self._directives = []
        response = {"_response": self._status}
        response.update(asdict(self))
        if transform:
            response = {snake2camel(key, exceptions=("_",)): value for key, value in response.items() if
                        keep_concealed or not key.startswith("_")}
        else:
            response = {key: value for key, value in response.items() if keep_concealed or not key.startswith("_")}

        return response

    def get_status(self) -> ResponseStatus:
        return self._status

    def set_status(self, status: ResponseStatus) -> None:
        self._status = status

    def __setattr__(self, key: str, value: Any) -> None:
        if self._changed is not None and not self._changed:
            super().__setattr__("_changed", True)
            super().__setattr__("_status", ResponseStatus.OK)
        super().__setattr__(key, value)

    def __post_init__(self) -> None:
        self._directives = []
        self._changed = False  # Have to by the last

    def has_changed(self) -> bool:
        return self._changed

    def add_directive(self, directive: ResponseDirective) -> None:
        if self._changed is not None and not self._changed:
            super().__setattr__("_changed", True)
            super().__setattr__("_status", ResponseStatus.OK)
        self._directives.append(directive)

    def get_directives(self) -> list[ResponseDirective]:
        return self._directives


def _response(cls, init, repr, eq, order, unsafe_hash, frozen, match_args, kw_only, slots, weakref_slot):
    if not any(issubclass(base, Response) for base in cls.__bases__):
        cls = type(cls.__name__, (Response, cls), dict(cls.__dict__))

    for name, value in cls.__annotations__.items():
        attribute = getattr(cls, name, False)
        if attribute:
            setattr(cls, name, field(init=False, default=attribute))
        else:
            setattr(cls, name, field(init=False, default=None))

    dataclass = _process_class(cls, init=init, repr=repr, eq=eq, order=order, unsafe_hash=unsafe_hash, frozen=frozen, match_args=match_args,
                               kw_only=kw_only, slots=slots, weakref_slot=weakref_slot)

    return dataclass


def response(cls=None, /, *, init=True, repr=True, eq=True, order=False, unsafe_hash=False, frozen=False,
             match_args=True, kw_only=False, slots=False, weakref_slot=None):
    def wrapper(cls):
        return _response(cls, init=init, repr=repr, eq=eq, order=order, unsafe_hash=unsafe_hash,
                         frozen=frozen, match_args=match_args, kw_only=kw_only, slots=slots, weakref_slot=weakref_slot)

    if cls is None:
        return wrapper  # pragma: no cover

    return wrapper(cls)
