import io
import os
import random
import uuid
from datetime import date, datetime, timedelta

import pytest
from faker import Faker
from pytest_factoryboy import register
from werkzeug.datastructures import FileStorage

from app.extensions.utils.enum.aws_enum import CloudFrontEnum, S3PathEnum
from app.extensions.utils.time_helper import get_str_from_today
from core.domains.community.dto.community_dto import (
    CreatePostDto,
    CreatePostAttachmentDto,
    UpdatePostDto,
)
from core.domains.cs.dto.cs_dto import (
    CreateCustomerInquiryDto,
    CreateInquiryAttachmentDto,
)
from core.domains.oauth.enum.oauth_enum import ProviderEnum
from core.domains.plant.enum.plant_enum import PlantCategoryEnum
from core.domains.user.dto.user_dto import CreateUserDto, UpdateUserDto
from core.domains.user.enum.user_enum import MobileOSEnum
from tests.seeder.factory import (
    UserFactory,
    DeviceFactory,
    NotificationTokenFactory,
    ReceivePushTypeFactory,
    UserProfileFactory,
    JwtFactory,
    BlacklistFactory,
    PlantProfileFactory,
    PostFactory,
    PostLikeStatusFactory,
    CommentFactory,
    CommentLikeStatusFactory,
    PostAttachmentFactory,
    PostCategoryFactory,
    BannedWordFactory,
    PlantCategoryFactory,
    PlantInfoFactory,
    fake,
)

MODEL_FACTORIES = [
    UserFactory,
    DeviceFactory,
    NotificationTokenFactory,
    ReceivePushTypeFactory,
    UserProfileFactory,
    JwtFactory,
    BlacklistFactory,
    PlantProfileFactory,
    PlantCategoryFactory,
    PlantInfoFactory,
    PostFactory,
    PostLikeStatusFactory,
    PostAttachmentFactory,
    PostCategoryFactory,
    BannedWordFactory,
    CommentFactory,
    CommentLikeStatusFactory,
]

faker = Faker()

for factory in MODEL_FACTORIES:
    register(factory_class=factory)


@pytest.fixture
def create_user(session, user_factory):
    user = user_factory.build(device=True, plant_profile=True)
    session.add(user)
    session.commit()

    return user


@pytest.fixture
def create_users(session, user_factory):
    users = user_factory.build_batch(device=True, plant_profile=True, size=2)
    session.add_all(users)
    session.commit()

    return users


@pytest.fixture(scope="function")
def create_post(session, post_factory, post_category_factory):
    post = post_factory.build(
        comments=True, post_like_statuses=True, post_attachments=True
    )

    post_cateroy1 = post_category_factory.build(id=1, name="관엽식물 커뮤니티")
    post_cateroy2 = post_category_factory.build(id=2, name="원예식물 커뮤니티")
    post_cateroy3 = post_category_factory.build(id=3, name="다육식물 커뮤니티")
    post_cateroy4 = post_category_factory.build(id=4, name="채소 커뮤니티")

    post_categories = [post_cateroy1, post_cateroy2, post_cateroy3, post_cateroy4]

    session.add(post)
    session.add_all(post_categories)
    session.commit()

    return post


@pytest.fixture
def create_user_param():
    file_name = fake.file_name()
    file_uuid = str(uuid.uuid4())
    f, extension = os.path.splitext(file_name)

    object_name = S3PathEnum.PROFILE_IMGS.value + file_uuid + extension
    path = f"{CloudFrontEnum.PLANTRA_CLOUD_FRONT_DOMAIN.value}/{object_name}"

    create_user_dict = dict(
        nickname=fake.name(),
        email="test@plantra.io",
        provider=random.choice(ProviderEnum.list()),
        provider_id=f"provider_id {fake.name()}",
        mobile_os=random.choice(MobileOSEnum.list()),
        start_growing_date=get_str_from_today(),
        plant_name=fake.name(),
        plant_category_id=random.choice([1, 2, 3, 4]),
        private_user_info_yn=True,
        required_terms_yn=True,
        receive_marketing_yn=False,
        file_extension=extension,
        file_name=file_name,
        file_path=f"{path}",
        file_uuid=file_uuid,
        login_uuid=str(uuid.uuid4()),
    )

    return create_user_dict


@pytest.fixture
def create_user_dto():
    file_name = fake.file_name()
    file_uuid = str(uuid.uuid4())
    f, extension = os.path.splitext(file_name)

    object_name = S3PathEnum.PROFILE_IMGS.value + file_uuid + extension
    path = f"{CloudFrontEnum.PLANTRA_CLOUD_FRONT_DOMAIN.value}/{object_name}"

    create_user_dto = CreateUserDto(
        nickname=fake.name(),
        email="test@plantra.io",
        provider=random.choice(ProviderEnum.list()),
        provider_id=f"provider_id {fake.name()}",
        mobile_os=random.choice(MobileOSEnum.list()),
        start_growing_date=get_str_from_today(),
        plant_name=fake.name(),
        plant_category_id=random.choice([1, 2, 3, 4]),
        private_user_info_yn=True,
        required_terms_yn=True,
        receive_marketing_yn=False,
        file_extension=extension,
        file_name=file_name,
        file_path=f"{path}",
        file_uuid=file_uuid,
        login_uuid=str(uuid.uuid4()),
    )

    return create_user_dto


@pytest.fixture
def update_user_dto():
    file_name = fake.file_name()
    file_uuid = str(uuid.uuid4())
    f, extension = os.path.splitext(file_name)

    object_name = S3PathEnum.PROFILE_IMGS.value + file_uuid + extension
    path = f"{CloudFrontEnum.PLANTRA_CLOUD_FRONT_DOMAIN.value}/{object_name}"

    update_user_dto = UpdateUserDto(
        user_id=1,
        user_nickname="user nickname",
        plant_nickname="plant_nickname",
        plant_category_id=random.choice([1, 2, 3, 4]),
        start_growing_date=get_str_from_today(),
        file_extension=extension,
        file_name=file_name,
        file_path=f"{path}",
        file_uuid=file_uuid,
    )

    return update_user_dto


@pytest.fixture
def update_post_dto(create_user, create_post):
    files = list()
    for _ in range(0, 3):
        file = FileStorage(
            stream=io.BytesIO(b"aaa"),
            filename="/Users/noah/Downloads/aa.png",
            content_type="multipart/form-data",
        )
        files.append(file)

    update_post_dto = UpdatePostDto(
        post_id=create_post.id,
        user_id=create_user.id,
        post_category_id=random.choice([1, 2, 3, 4]),
        body=faker.sentence(),
        files=files,
        delete_files=[1, 2],
    )

    return update_post_dto


@pytest.fixture
def create_post_dto(create_user):
    files = list()
    for _ in range(0, 3):
        file = fake.file_name()
        files.append(file)

    create_post_dto = CreatePostDto(
        user_id=create_user.id,
        post_category_id=random.choice([1, 2, 3, 4]),
        body=faker.sentence(),
        files=files,
    )

    return create_post_dto


@pytest.fixture
def create_cs_dto(create_user):
    files = list()
    for _ in range(0, 3):
        file = fake.file_name()
        files.append(file)

    create_cs_dto = CreateCustomerInquiryDto(
        user_id=create_user.id,
        title=faker.sentence(),
        email=faker.email(),
        body=faker.sentence(),
        files=files,
    )

    return create_cs_dto


@pytest.fixture
def create_inquiry_attachment_dtos():
    dtos = list()
    inquiry_id = 1

    for _ in range(0, 3):
        file = FileStorage(
            stream=io.BytesIO(b"aaa"),
            filename=f"/Users/noah/Downloads/{fake.file_name()}.png",
            content_type="multipart/form-data",
        )

        file_uuid = str(uuid.uuid4())
        f, extension = os.path.splitext(file.filename)

        object_name = S3PathEnum.PROFILE_IMGS.value + file_uuid + extension
        path = f"{CloudFrontEnum.PLANTRA_CLOUD_FRONT_DOMAIN.value}/{object_name}"

        create_post_attachment_dto = CreateInquiryAttachmentDto(
            inquiry_id=inquiry_id,
            uuid=file_uuid,
            file_name=f,
            path=path,
            extension=extension,
            object_name=object_name,
            origin_file=file,
            is_upload=True,  # mock 처리 할 것이기 때문에 True로 처리
        )
        dtos.append(create_post_attachment_dto)

    return dtos


@pytest.fixture
def update_post_dto(create_user, create_post):
    files = list()
    for _ in range(0, 3):
        file = FileStorage(
            stream=io.BytesIO(b"aaa"),
            filename=f"/Users/noah/Downloads/{fake.file_name()}.png",
            content_type="multipart/form-data",
        )

        files.append(file)

    update_post_dto = UpdatePostDto(
        post_id=create_post.id,
        user_id=create_user.id,
        post_category_id=random.choice([1, 2, 3, 4]),
        body=faker.sentence(),
        files=files,
        delete_files=[1, 2],
    )

    return update_post_dto


@pytest.fixture
def create_post_attachment_dtos():
    dtos = list()
    post_id = 1

    for _ in range(0, 3):
        file = FileStorage(
            stream=io.BytesIO(b"aaa"),
            filename=f"/Users/noah/Downloads/{fake.file_name()}.png",
            content_type="multipart/form-data",
        )

        file_uuid = str(uuid.uuid4())
        f, extension = os.path.splitext(file.filename)

        object_name = S3PathEnum.PROFILE_IMGS.value + file_uuid + extension
        path = f"{CloudFrontEnum.PLANTRA_CLOUD_FRONT_DOMAIN.value}/{object_name}"

        create_post_attachment_dto = CreatePostAttachmentDto(
            post_id=post_id,
            uuid=file_uuid,
            file_name=f,
            path=path,
            extension=extension,
            object_name=object_name,
            origin_file=file,
            is_upload=True,  # mock 처리 할 것이기 때문에 True로 처리
        )
        dtos.append(create_post_attachment_dto)

    return dtos


@pytest.fixture
def create_plant_cateroies(session, plant_category_factory):
    plant_cateroy1 = plant_category_factory.build(
        name=PlantCategoryEnum.HOUSE_PLANT.value
    )
    plant_cateroy2 = plant_category_factory.build(
        name=PlantCategoryEnum.HORTICULTURAL_PLANT.value
    )
    plant_cateroy3 = plant_category_factory.build(
        name=PlantCategoryEnum.SUCCULENT_PLANT.value
    )
    plant_cateroy4 = plant_category_factory.build(
        name=PlantCategoryEnum.VEGETABLE.value
    )

    plant_categories = [plant_cateroy1, plant_cateroy2, plant_cateroy3, plant_cateroy4]

    session.add_all(plant_categories)
    session.commit()

    return plant_categories


@pytest.fixture
def create_banned_word(session, banned_word_factory):
    banned_word = banned_word_factory.build()
    session.add(banned_word)
    session.commit()

    return banned_word


def make_random_today_date(between_days: int = 1, year_ago: int = 2):
    """
        주어진 날의 00:00:00 ~ 23:59:59 사이의 랜덤 시간을 만든다.
        기본적으로 조회시간 기준 2년전으로 만듬
    """
    return faker.date_time_between_dates(
        datetime_start=date.today() - timedelta(days=365 * year_ago + between_days),
        datetime_end=datetime.now() - timedelta(days=365 * year_ago + between_days),
    )


def make_random_between_date(start_date, end_date):
    """주어진 날짜 사이의 랜덤 date 만든다 """
    return faker.date_time_between_dates(
        datetime_start=start_date, datetime_end=end_date
    )
