from enum import Enum

from marshmallow import Schema, fields, post_dump, validate
from marshmallow_enum import EnumField

__all__ = [
    "StrictSchema",
    "TaskSchema",
    "FindTaskSchema",
    "UpdateTaskSchema",
    "with_schemas",
]


class StrictSchema(Schema):
    def __init__(self, *args, **kwargs):
        kwargs["strict"] = True
        super().__init__(*args, **kwargs)


class TaskSchema(StrictSchema):
    id = fields.Integer(required=True)
    timing_from = fields.DateTime(required=True)
    timing_to = fields.DateTime(required=True)


class FindTaskSchema(StrictSchema):
    executor_id = fields.String(
        required=True,
        validate=validate.Length(min=1, error="Value should not be empty."),
    )

    @post_dump
    def _dump_validate(self, data: dict) -> dict:
        self.validate(data)
        return data


class UpdateTaskSchema(StrictSchema):
    task_id = fields.Integer(required=True)
    status = EnumField(Enum)
    executor_id = fields.String(
        required=True,
        validate=validate.Length(min=1, error="Value should not be empty."),
    )

    @post_dump
    def _dump_validate(self, data: dict) -> dict:
        self.validate(data)
        return data


class with_schemas:
    """Decorator for serializing input arguments and deserializing returned
    value."""

    def __init__(self, input_schema: Schema, output_schema: Schema):
        self.input_schema = input_schema
        self.output_schema = output_schema

    def __call__(self, func):
        async def wrapped(s, **kwargs):
            dumped = self.input_schema().dump(kwargs).data
            got = await func(s, **dumped)
            return self.output_schema().load(got).data

        return wrapped
