from typing import List

from pydantic import (
    BaseModel,
    ValidationError,
    StrictInt,
    StrictStr,
    StrictBool,
    validator,
)
from werkzeug.datastructures import FileStorage

from app.extensions.utils.enum.image_enum import ImageFormatEnum, ImageSizeEnum
from app.extensions.utils.log_helper import logger_
from core.domains.community.dto.community_dto import (
    CreatePostDto,
    UpdatePostDto,
    DeletePostDto,
    GetPostDto,
    UpdatePostLikeStatusDto,
)
from core.exceptions import InvalidRequestException

logger = logger_.getLogger(__name__)


class CreatePostSchema(BaseModel):
    user_id: StrictInt
    post_category_id: StrictInt
    body: StrictStr


class UpdatePostSchema(BaseModel):
    post_id: StrictInt
    user_id: StrictInt
    post_category_id: StrictInt
    body: StrictStr
    delete_files: List


class DeletePostSchema(BaseModel):
    post_id: StrictInt
    user_id: StrictInt


class GetPostSchema(BaseModel):
    post_id: StrictInt
    user_id: StrictInt


class UpdatePostLikeStatusSchema(BaseModel):
    post_id: StrictInt
    user_id: StrictInt
    is_liked: StrictBool


class ImageListSchema(BaseModel):
    files: List[FileStorage]

    @validator("files")
    def image_validation(cls, files: List[FileStorage]):
        try:
            for file in files:
                extension: str = file.filename.split(".")[1]
                if extension.lower() not in ImageFormatEnum.list():
                    raise ValidationError(
                        "allowed image extension format: jpg, jpeg, png"
                    )

                bytes_size = ImageListSchema.get_bytes_size(cls=cls, file=file)
                if bytes_size > ImageSizeEnum.ALLOWED_MAX_SIZE_BYTES.value:
                    raise ValidationError(
                        f"file size over {ImageSizeEnum.ALLOWED_MAX_SIZE_MEGABYTES.value}MB"
                    )
                file.stream.seek(0)
            return files
        except IndexError:
            raise ValidationError("not found extension format")
        except Exception as e:
            raise ValidationError(f"unexpected error: {e}")

    def get_bytes_size(cls, file: FileStorage):
        return len(file.read())

    class Config:
        arbitrary_types_allowed = True


class CreatePostRequest:
    def __init__(
        self, user_id, post_category_id, body, files,
    ):
        self.user_id = int(user_id) if user_id else None
        self.post_category_id = int(post_category_id) if post_category_id else None
        self.body = body
        self.files = files

    def validate_request_and_make_dto(self):
        try:
            schema = CreatePostSchema(
                user_id=self.user_id,
                post_category_id=self.post_category_id,
                body=self.body,
            ).dict()
            file_list_schema = ImageListSchema(files=self.files)

            schema.update(file_list_schema.dict())
            return CreatePostDto(**schema)
        except ValidationError as e:
            logger.error(
                f"[CreatePostRequest][validate_request_and_make_dto] error : {e}"
            )
            raise InvalidRequestException(message=e.errors())


class UpdatePostRequest:
    def __init__(self, user_id, post_id, post_category_id, body, files, delete_files):
        self.post_id = int(post_id) if post_id else None
        self.user_id = int(user_id) if user_id else None
        self.post_category_id = int(post_category_id) if post_category_id else None
        self.body = body
        self.files = files
        self.delete_files = delete_files

    def validate_request_and_make_dto(self):
        try:
            schema = UpdatePostSchema(
                post_id=self.post_id,
                user_id=self.user_id,
                post_category_id=self.post_category_id,
                body=self.body,
                delete_files=self.delete_files,
            ).dict()

            file_list_schema = ImageListSchema(files=self.files)

            schema.update(file_list_schema.dict())
            return UpdatePostDto(**schema)
        except ValidationError as e:
            logger.error(
                f"[UpdatePostRequest][validate_request_and_make_dto] error : {e}"
            )
            raise InvalidRequestException(message=e.errors())


class DeletePostRequest:
    def __init__(self, user_id, post_id):
        self.post_id = int(post_id) if post_id else None
        self.user_id = int(user_id) if user_id else None

    def validate_request_and_make_dto(self):
        try:
            schema = DeletePostSchema(
                post_id=self.post_id, user_id=self.user_id,
            ).dict()
            return DeletePostDto(**schema)
        except ValidationError as e:
            logger.error(
                f"[DeletePostRequest][validate_request_and_make_dto] error : {e}"
            )
            raise InvalidRequestException(message=e.errors())


class GetPostRequest:
    def __init__(self, user_id, post_id):
        self.post_id = int(post_id) if post_id else None
        self.user_id = int(user_id) if user_id else None

    def validate_request_and_make_dto(self):
        try:
            schema = GetPostSchema(post_id=self.post_id, user_id=self.user_id,).dict()
            return GetPostDto(**schema)
        except ValidationError as e:
            logger.error(f"[GetPostRequest][validate_request_and_make_dto] error : {e}")
            raise InvalidRequestException(message=e.errors())


class UpdatePostLikeStatusRequest:
    def __init__(self, user_id, post_id, is_liked):
        self.post_id = int(post_id) if post_id else None
        self.user_id = int(user_id) if user_id else None
        self.is_liked = is_liked

    def validate_request_and_make_dto(self):
        try:
            schema = UpdatePostLikeStatusSchema(
                post_id=self.post_id, user_id=self.user_id, is_liked=self.is_liked
            ).dict()
            return UpdatePostLikeStatusDto(**schema)
        except ValidationError as e:
            logger.error(
                f"[UpdatePostLikeStatusRequest][validate_request_and_make_dto] error : {e}"
            )
            raise InvalidRequestException(message=e.errors())
