import io
import json
import os
import random
import uuid
from unittest.mock import patch

from faker import Faker
from flask import url_for
from werkzeug.datastructures import FileStorage

from app.extensions.utils.enum.aws_enum import S3PathEnum, CloudFrontEnum
from app.persistence.model import PostModel
from core.domains.community.dto.community_dto import CreatePostAttachmentDto

faker = Faker()


@patch(
    "core.domains.community.use_case.v1.community_use_case.CreatePostUseCase._upload_post_attachments",
    return_value=None,
)
def test_view_create_post_when_with_files_then_success(
    _upload_post_attachments,
    client,
    session,
    test_request_context,
    make_header,
    make_authorization,
    create_user,
):
    authorization = make_authorization(user_id=create_user.id)
    headers = make_header(
        authorization=authorization, content_type="multipart/form-data", accept="*/*"
    )

    # 실제 업로드 확인하려면 아래 경로에 이미지 첨부하고 patch 데코레이터 제거한 뒤 실행.
    file1 = FileStorage(
        stream=io.BytesIO(b"aaa"),
        filename="/Users/noah/Downloads/aa.png",
        content_type="multipart/form-data",
    )
    file2 = FileStorage(
        stream=io.BytesIO(b"aaa"),
        filename="/Users/noah/Downloads/bb.jpeg",
        content_type="multipart/form-data",
    )

    param = dict(
        post_category_id=random.choice([1, 2, 3, 4]),
        body=faker.sentence(),
        files=[file1, file2],
    )

    with test_request_context:
        response = client.post(
            url_for("api/groot.create_post"), headers=headers, data=param
        )

    result = response.get_json()["data"]["result"]
    assert response.status_code == 200
    assert result == "success"


def test_view_create_post_when_without_files_then_success(
    client, session, test_request_context, make_header, make_authorization, create_user,
):
    authorization = make_authorization(user_id=create_user.id)
    headers = make_header(
        authorization=authorization, content_type="multipart/form-data", accept="*/*"
    )

    param = dict(
        post_category_id=random.choice([1, 2, 3, 4]), body=faker.sentence(), files=[],
    )

    with test_request_context:
        response = client.post(
            url_for("api/groot.create_post"), headers=headers, data=param
        )

    result = response.get_json()["data"]["result"]
    assert response.status_code == 200
    assert result == "success"


def test_view_create_post_when_over_5_picture_then_failure(
    client, session, test_request_context, make_header, make_authorization, create_user,
):
    authorization = make_authorization(user_id=create_user.id)
    headers = make_header(
        authorization=authorization, content_type="multipart/form-data", accept="*/*"
    )

    files = list()
    for _ in range(0, 6):
        file = FileStorage(
            stream=io.BytesIO(b"aaa"),
            filename="/Users/noah/Downloads/aa.png",
            content_type="multipart/form-data",
        )
        files.append(file)

    param = dict(
        post_category_id=random.choice([1, 2, 3, 4]),
        body=faker.sentence(),
        files=files,
    )

    with test_request_context:
        response = client.post(
            url_for("api/groot.create_post"), headers=headers, data=param
        )

    result = response.get_json()
    assert response.status_code == 400
    assert result.get("detail") == "over 5 picture"
    assert result.get("message") == "invalid_request_error"


def test_view_create_post_when_empty_body_then_failure(
    client, session, test_request_context, make_header, make_authorization, create_user,
):
    authorization = make_authorization(user_id=create_user.id)
    headers = make_header(
        authorization=authorization, content_type="multipart/form-data", accept="*/*"
    )

    param = dict(post_category_id=random.choice([1, 2, 3, 4]), body="", files=[],)

    with test_request_context:
        response = client.post(
            url_for("api/groot.create_post"), headers=headers, data=param
        )

    result = response.get_json()
    assert response.status_code == 400
    assert result.get("detail") == "empty body"
    assert result.get("message") == "invalid_request_error"


def test_view_create_post_when_over_char_body_then_failure(
    client, session, test_request_context, make_header, make_authorization, create_user,
):
    authorization = make_authorization(user_id=create_user.id)
    headers = make_header(
        authorization=authorization, content_type="multipart/form-data", accept="*/*"
    )

    param = dict(
        post_category_id=random.choice([1, 2, 3, 4]), body="a" * 201, files=[],
    )

    with test_request_context:
        response = client.post(
            url_for("api/groot.create_post"), headers=headers, data=param
        )

    result = response.get_json()
    assert response.status_code == 400
    assert result.get("detail") == "over 200 char"
    assert result.get("message") == "invalid_request_error"


def test_view_create_post_when_banned_word_in_body_then_failure(
    client,
    session,
    test_request_context,
    make_header,
    make_authorization,
    create_user,
    create_banned_word,
):
    authorization = make_authorization(user_id=create_user.id)
    headers = make_header(
        authorization=authorization, content_type="multipart/form-data", accept="*/*"
    )

    param = dict(
        post_category_id=random.choice([1, 2, 3, 4]), body="밴워드 테스트 시발 ㅋ", files=[],
    )

    with test_request_context:
        response = client.post(
            url_for("api/groot.create_post"), headers=headers, data=param
        )

    result = response.get_json()
    assert response.status_code == 400
    assert result.get("detail") == "banned word"
    assert result.get("message") == "invalid_request_error"


# @patch(
#     "core.domains.community.use_case.v1.community_use_case.UpdatePostUseCase._upload_post_attachments",
#     return_value=None,
# )
# @patch(
#     "core.domains.community.use_case.v1.community_use_case.UpdatePostUseCase._get_file_split_object",
# )
# @patch(
#     "core.domains.community.repository.community_repository.CommunityRepository.get_post_by_id"
# )
# def test_view_update_post_when_with_files_then_success(
#     get_post_by_id,
#     _get_file_split_object,
#     _upload_post_attachments,
#     client,
#     session,
#     test_request_context,
#     make_header,
#     make_authorization,
#     create_user,
#     create_post,
# ):
#     """
#     기존 사진2개 -> 추가 사진2개 등록 = 총 사진4개
#     -> 기존 사진 is_available = False
#     -> 새 사진 is_available = True
#     """
#
#     get_post_by_id.return_value = create_post
#
#     authorization = make_authorization(user_id=create_post.user_id)
#     headers = make_header(
#         authorization=authorization, content_type="multipart/form-data", accept="*/*"
#     )
#
#     # 실제 업로드 확인하려면 아래 경로에 이미지 첨부하고 patch 데코레이터 제거한 뒤 실행.
#     file1 = FileStorage(
#         stream=io.BytesIO(b"aaa"),
#         filename="/Users/noah/Downloads/aa.png",
#         content_type="multipart/form-data",
#     )
#     file2 = FileStorage(
#         stream=io.BytesIO(b"aaa"),
#         filename="/Users/noah/Downloads/bb.jpeg",
#         content_type="multipart/form-data",
#     )
#     files = [file1, file2]
#
#     param = dict(
#         post_category_id=random.choice([1, 2, 3, 4]),
#         body=faker.sentence(),
#         files=files,
#         delete_files=[1, 2],
#     )
#
#     with test_request_context:
#         create_post_attachment_dtos = list()
#         for file in files:
#             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=create_post.id,
#                 uuid=file_uuid,
#                 file_name=f,
#                 path=path,
#                 extension=extension,
#                 object_name=object_name,
#                 origin_file=file,
#                 is_upload=True,  # mock 처리 할 것이기 때문에 True로 처리
#             )
#             create_post_attachment_dtos.append(create_post_attachment_dto)
#
#         _get_file_split_object.return_value = create_post_attachment_dtos
#
#         response = client.patch(
#             url_for("api/groot.update_post", post_id=create_post.id),
#             headers=headers,
#             data=param,
#         )
#
#     query_result = session.query(PostModel).filter_by(id=create_post.id).first()
#
#     result = response.get_json()["data"]["result"]
#     assert response.status_code == 200
#     assert result == "success"
#     assert len(query_result.post_attachments) == 4
#     assert query_result.post_attachments[0].is_available == False
#     assert query_result.post_attachments[1].is_available == False
#     assert query_result.post_attachments[2].is_available == True
#     assert query_result.post_attachments[3].is_available == True


@patch(
    "core.domains.community.use_case.v1.community_use_case.UpdatePostUseCase._upload_post_attachments",
    return_value=None,
)
@patch(
    "core.domains.community.repository.community_repository.CommunityRepository.get_post_by_id"
)
def test_view_update_post_when_without_permission_then_success(
    get_post_by_id,
    _upload_post_attachments,
    client,
    session,
    test_request_context,
    make_header,
    make_authorization,
    create_post,
):
    get_post_by_id.return_value = create_post.to_entity()

    authorization = make_authorization(user_id=create_post.user_id + 1)
    headers = make_header(
        authorization=authorization, content_type="multipart/form-data", accept="*/*"
    )

    param = dict(
        post_category_id=random.choice([1, 2, 3, 4]),
        body=faker.sentence(),
        files=[],
        delete_files=[],
    )

    with test_request_context:
        response = client.patch(
            url_for("api/groot.update_post", post_id=create_post.id),
            headers=headers,
            data=param,
        )

    result = response.get_json()
    assert response.status_code == 400
    assert result.get("detail") == "not permission"
    assert result.get("message") == "invalid_request_error"


@patch(
    "core.domains.community.repository.community_repository.CommunityRepository.get_post_by_id"
)
def test_view_delete_post_then_success(
    get_post_by_id,
    client,
    session,
    test_request_context,
    make_header,
    make_authorization,
    create_post,
):
    get_post_by_id.return_value = create_post.to_entity()

    authorization = make_authorization(user_id=create_post.user_id)
    headers = make_header(
        authorization=authorization, content_type="multipart/form-data", accept="*/*"
    )

    with test_request_context:
        response = client.delete(
            url_for("api/groot.delete_post", post_id=create_post.id), headers=headers,
        )

    result = response.get_json()["data"]["result"]
    assert response.status_code == 200
    assert result == "success"


@patch(
    "core.domains.community.repository.community_repository.CommunityRepository.get_post_by_id"
)
def test_view_delete_post_when_without_permission_then_success(
    get_post_by_id,
    client,
    session,
    test_request_context,
    make_header,
    make_authorization,
    create_post,
):
    get_post_by_id.return_value = create_post.to_entity()

    authorization = make_authorization(user_id=create_post.user_id + 1)
    headers = make_header(
        authorization=authorization, content_type="multipart/form-data", accept="*/*"
    )

    with test_request_context:
        response = client.delete(
            url_for("api/groot.delete_post", post_id=create_post.id), headers=headers,
        )

    result = response.get_json()
    assert response.status_code == 400
    assert result.get("detail") == "not permission"
    assert result.get("message") == "invalid_request_error"


@patch(
    "core.domains.community.repository.community_repository.CommunityRepository.get_post_by_id"
)
def test_view_get_post_then_success(
    get_post_by_id,
    client,
    session,
    test_request_context,
    make_header,
    make_authorization,
    create_post,
):
    get_post_by_id.return_value = create_post.to_entity()

    authorization = make_authorization(user_id=create_post.user_id)
    headers = make_header(
        authorization=authorization,
        content_type="application/json",
        accept="application/json",
    )

    with test_request_context:
        response = client.get(
            url_for("api/groot.get_post", post_id=create_post.id), headers=headers,
        )

    result = response.get_json()["data"]["post"]
    assert response.status_code == 200
    assert result.get("body") == create_post.body
    assert (
        result.get("post_attachments")[0].get("path")
        == create_post.post_attachments[0].path
    )


def test_view_create_comment_when_empty_body_then_failure(
    client,
    session,
    test_request_context,
    make_header,
    make_authorization,
    create_user,
    create_post,
):
    authorization = make_authorization(user_id=create_user.id)
    headers = make_header(
        authorization=authorization,
        content_type="application/json",
        accept="application/json",
    )

    param = dict(post_id=create_post.id, body="")

    with test_request_context:
        response = client.post(
            url_for("api/groot.create_comment"),
            headers=headers,
            data=json.dumps(param),
        )

    result = response.get_json()
    assert response.status_code == 400
    assert result.get("detail") == "empty body"
    assert result.get("message") == "invalid_request_error"


def test_view_create_comment_when_over_char_body_then_failure(
    client,
    session,
    test_request_context,
    make_header,
    make_authorization,
    create_user,
    create_post,
):
    authorization = make_authorization(user_id=create_user.id)
    headers = make_header(
        authorization=authorization,
        content_type="application/json",
        accept="application/json",
    )

    param = dict(post_id=create_post.id, body="가" * 201)

    with test_request_context:
        response = client.post(
            url_for("api/groot.create_comment"),
            headers=headers,
            data=json.dumps(param),
        )

    result = response.get_json()
    assert response.status_code == 400
    assert result.get("detail") == "over 200 char"
    assert result.get("message") == "invalid_request_error"


def test_view_create_comment_when_banned_word_in_body_then_failure(
    client,
    session,
    test_request_context,
    make_header,
    make_authorization,
    create_user,
    create_post,
    create_banned_word,
):
    authorization = make_authorization(user_id=create_user.id)
    headers = make_header(
        authorization=authorization,
        content_type="application/json",
        accept="application/json",
    )

    param = dict(post_id=create_post.id, body="금지어--시발")

    with test_request_context:
        response = client.post(
            url_for("api/groot.create_comment"),
            headers=headers,
            data=json.dumps(param),
        )

    result = response.get_json()
    assert response.status_code == 400
    assert result.get("detail") == "banned word"
    assert result.get("message") == "invalid_request_error"


def test_view_update_comment_then_success(
    client,
    session,
    test_request_context,
    make_header,
    make_authorization,
    create_user,
    create_post,
):
    authorization = make_authorization(user_id=create_post.user_id)
    headers = make_header(
        authorization=authorization,
        content_type="application/json",
        accept="application/json",
    )

    param = dict(body=faker.sentence(),)

    with test_request_context:
        response = client.patch(
            url_for("api/groot.update_comment", comment_id=create_post.comments[0].id),
            headers=headers,
            data=json.dumps(param),
        )

    result = response.get_json()["data"]["result"]
    assert response.status_code == 200
    assert result == "success"


def test_view_update_comment_when_without_permission_then_success(
    client,
    session,
    test_request_context,
    make_header,
    make_authorization,
    create_user,
    create_post,
):
    authorization = make_authorization(user_id=create_post.comments[0].user_id + 1)
    headers = make_header(
        authorization=authorization,
        content_type="application/json",
        accept="application/json",
    )

    param = dict(body=faker.sentence(),)

    with test_request_context:
        response = client.patch(
            url_for("api/groot.update_comment", comment_id=create_post.comments[0].id),
            headers=headers,
            data=json.dumps(param),
        )

    result = response.get_json()
    assert response.status_code == 400
    assert result.get("detail") == "not permission"
    assert result.get("message") == "invalid_request_error"


def test_view_delete_comment_then_success(
    client,
    session,
    test_request_context,
    make_header,
    make_authorization,
    create_user,
    create_post,
):
    authorization = make_authorization(user_id=create_post.user_id)
    headers = make_header(
        authorization=authorization,
        content_type="application/json",
        accept="application/json",
    )

    with test_request_context:
        response = client.delete(
            url_for("api/groot.delete_comment", comment_id=create_post.comments[0].id),
            headers=headers,
        )

    result = response.get_json()["data"]["result"]
    assert response.status_code == 200
    assert result == "success"


def test_view_delete_comment_when_without_permission_then_success(
    client,
    session,
    test_request_context,
    make_header,
    make_authorization,
    create_user,
    create_post,
):
    authorization = make_authorization(user_id=create_post.user_id + 1)
    headers = make_header(
        authorization=authorization,
        content_type="application/json",
        accept="application/json",
    )

    with test_request_context:
        response = client.delete(
            url_for("api/groot.delete_comment", comment_id=create_post.comments[0].id),
            headers=headers,
        )

    result = response.get_json()
    assert response.status_code == 400
    assert result.get("detail") == "not permission"
    assert result.get("message") == "invalid_request_error"


def test_view_update_comment_like_status_when_insert_then_success(
    client,
    session,
    test_request_context,
    make_header,
    make_authorization,
    create_user,
    create_post,
):
    authorization = make_authorization(user_id=create_post.user_id + 1)
    headers = make_header(
        authorization=authorization,
        content_type="application/json",
        accept="application/json",
    )

    param = dict(is_liked=True)
    with test_request_context:
        response = client.post(
            url_for(
                "api/groot.update_comment_like_status",
                comment_id=create_post.comments[0].id,
            ),
            headers=headers,
            data=json.dumps(param),
        )

    result = response.get_json()["data"]["result"]
    assert response.status_code == 200
    assert result == "success"
