from pydantic import (
    BaseModel,
    StrictStr,
    StrictInt,
    StrictBool,
    ValidationError,
    validator,
    UUID4,
)
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.user.dto.user_dto import (
    UploadUserProfileImgDto,
    CreateUserDto,
    GetUserDto,
    UpdateUserDto,
    UpdateReceivePushTypeDto,
    GetReceivePushTypeDto,
    UpdateDeviceDto,
    UpdateFCMTokenDto,
    GetMacDto,
)
from core.exceptions import InvalidRequestException

logger = logger_.getLogger(__name__)


class UploadUserProfileImageSchema(BaseModel):
    file: FileStorage

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

            bytes_size = UploadUserProfileImageSchema.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)
        except IndexError:
            raise ValidationError("not found extension format")
        return file

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

    class Config:
        arbitrary_types_allowed = True


class UploadUserProfileImgRequestSchema:
    def __init__(self, file: FileStorage):
        self.file = file

    def validate_request_and_make_dto(self):
        try:
            schema = UploadUserProfileImageSchema(file=self.file,).dict()
            return UploadUserProfileImgDto(**schema)
        except ValidationError as e:
            logger.error(
                f"[UploadUserProfileImgRequestSchema][validate_request_and_make_dto] error : {e}"
            )
            raise InvalidRequestException(message=e.errors())


class CreateUserSchema(BaseModel):
    nickname: StrictStr
    email: StrictStr | None
    provider: StrictStr
    provider_id: StrictStr
    mobile_os: StrictStr
    start_growing_date: StrictStr
    plant_name: StrictStr
    plant_category_id: StrictInt
    private_user_info_yn: StrictBool
    required_terms_yn: StrictBool
    receive_marketing_yn: StrictBool
    file_extension: StrictStr | None
    file_name: StrictStr | None
    file_path: StrictStr | None
    file_uuid: StrictStr | None
    login_uuid: UUID4

    @validator("login_uuid")
    def check_uuid(cls, login_uuid):
        return str(login_uuid)


class CreateUserRequestSchema:
    def __init__(
        self,
        nickname: str,
        email: str | None,
        provider: str,
        provider_id: str,
        mobile_os: str,
        start_growing_date: str,
        plant_name: str,
        plant_category_id: int,
        private_user_info_yn: bool,
        required_terms_yn: bool,
        receive_marketing_yn: bool,
        file_extension: str | None,
        file_name: str | None,
        file_path: str | None,
        file_uuid: str | None,
        login_uuid: str,
    ):
        self.nickname = nickname
        self.email = email
        self.provider = provider
        self.provider_id = provider_id
        self.mobile_os = mobile_os
        self.start_growing_date = start_growing_date
        self.plant_name = plant_name
        self.plant_category_id = plant_category_id
        self.private_user_info_yn = private_user_info_yn
        self.required_terms_yn = required_terms_yn
        self.receive_marketing_yn = receive_marketing_yn
        self.file_extension = file_extension
        self.file_name = file_name
        self.file_path = file_path
        self.file_uuid = file_uuid
        self.login_uuid = login_uuid

    def validate_request_and_make_dto(self):
        try:
            schema = CreateUserSchema(
                nickname=self.nickname,
                email=self.email,
                provider=self.provider,
                provider_id=self.provider_id,
                mobile_os=self.mobile_os,
                start_growing_date=self.start_growing_date,
                plant_name=self.plant_name,
                plant_category_id=self.plant_category_id,
                private_user_info_yn=self.private_user_info_yn,
                required_terms_yn=self.required_terms_yn,
                receive_marketing_yn=self.receive_marketing_yn,
                file_extension=self.file_extension,
                file_name=self.file_name,
                file_path=self.file_path,
                file_uuid=self.file_uuid,
                login_uuid=self.login_uuid,
            ).dict()
            return CreateUserDto(**schema)
        except ValidationError as e:
            logger.error(
                f"[CreateUserRequestSchema][validate_request_and_make_dto] error : {e}"
            )
            raise InvalidRequestException(message=e.errors())


class GetUserSchema(BaseModel):
    user_id: StrictInt


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

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


class UpdateUserSchema(BaseModel):
    user_id: StrictInt
    plant_category_id: StrictInt
    user_nickname: StrictStr
    plant_nickname: StrictStr
    start_growing_date: StrictStr
    file_extension: StrictStr | None
    file_name: StrictStr | None
    file_path: StrictStr | None
    file_uuid: StrictStr | None


class UpdateUserRequestSchema:
    def __init__(
        self,
        user_id: int,
        plant_category_id: int,
        user_nickname: str,
        plant_nickname: str,
        start_growing_date: str,
        file_extension: str | None,
        file_name: str | None,
        file_path: str | None,
        file_uuid: str | None,
    ):
        self.user_id = user_id
        self.plant_category_id = plant_category_id
        self.user_nickname = user_nickname
        self.plant_nickname = plant_nickname
        self.start_growing_date = start_growing_date
        self.file_extension = file_extension
        self.file_name = file_name
        self.file_path = file_path
        self.file_uuid = file_uuid

    def validate_request_and_make_dto(self):
        try:
            schema = UpdateUserSchema(
                user_id=self.user_id,
                plant_category_id=self.plant_category_id,
                user_nickname=self.user_nickname,
                plant_nickname=self.plant_nickname,
                start_growing_date=self.start_growing_date,
                file_extension=self.file_extension,
                file_name=self.file_name,
                file_path=self.file_path,
                file_uuid=self.file_uuid,
            ).dict()
            return UpdateUserDto(**schema)
        except ValidationError as e:
            logger.error(
                f"[UpdateUserRequestSchema][validate_request_and_make_dto] error : {e}"
            )
            raise InvalidRequestException(message=e.errors())


class UpdateReceivePushTypeSchema(BaseModel):
    user_id: StrictInt
    is_like: StrictBool
    is_comment: StrictBool
    is_incubator: StrictBool


class UpdateReceivePushTypeRequestSchema:
    def __init__(
        self, user_id: int, is_like: bool, is_comment: bool, is_incubator: bool
    ):
        self.user_id = int(user_id) if user_id else None
        self.is_like = is_like
        self.is_comment = is_comment
        self.is_incubator = is_incubator

    def validate_request_and_make_dto(self):
        try:
            schema = UpdateReceivePushTypeSchema(
                user_id=self.user_id,
                is_like=self.is_like,
                is_comment=self.is_comment,
                is_incubator=self.is_incubator,
            ).dict()
            return UpdateReceivePushTypeDto(**schema)
        except ValidationError as e:
            logger.error(
                f"[UpdateReceivePushTypeRequestSchema][validate_request_and_make_dto] error : {e}"
            )
            raise InvalidRequestException(message=e.errors())


class GetReceivePushTypeSchema(BaseModel):
    user_id: StrictInt


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

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


class UpdateDeviceSchema(BaseModel):
    user_id: StrictInt
    hw_mac: StrictStr


class UpdateDeviceRequestSchema:
    def __init__(self, user_id: int, hw_mac: str):
        self.user_id = int(user_id) if user_id else None
        self.hw_mac = hw_mac

    def validate_request_and_make_dto(self):
        try:
            schema = UpdateDeviceSchema(
                user_id=self.user_id, hw_mac=self.hw_mac,
            ).dict()
            return UpdateDeviceDto(**schema)
        except ValidationError as e:
            logger.error(
                f"[UpdateDeviceRequestSchema][validate_request_and_make_dto] error : {e}"
            )
            raise InvalidRequestException(message=e.errors())


class UpdateFCMTokenSchema(BaseModel):
    user_id: StrictInt
    fcm_token: StrictStr


class UpdateFCMTokenRequestSchema:
    def __init__(self, user_id: int, fcm_token: str):
        self.user_id = int(user_id) if user_id else None
        self.fcm_token = fcm_token

    def validate_request_and_make_dto(self):
        try:
            schema = UpdateFCMTokenSchema(
                user_id=self.user_id, fcm_token=self.fcm_token,
            ).dict()
            return UpdateFCMTokenDto(**schema)
        except ValidationError as e:
            logger.error(
                f"[UpdateFCMTokenRequestSchema][validate_request_and_make_dto] error : {e}"
            )
            raise InvalidRequestException(message=e.errors())


class GetMacSchema(BaseModel):
    user_id: StrictInt


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

    def validate_request_and_make_dto(self):
        try:
            schema = GetMacSchema(user_id=self.user_id).dict()
            return GetMacDto(**schema)
        except ValidationError as e:
            logger.error(
                f"[GetMacRequestSchema][validate_request_and_make_dto] error : {e}"
            )
            raise InvalidRequestException(message=e.errors())
