from pathlib import Path

from ichec_django_core.models import Member
from ichec_django_core.client import MemberIdentifierCreate
from ichec_django_core.client.resources import create_members

from ichec_django_core.test import (
    AuthAPITestCase,
    setup_default_users_and_groups,
    add_group_permissions,
)


class TestMemberView(AuthAPITestCase):
    template = create_members(1)[0]

    def setUp(self):
        self.url = "/api/members/"
        setup_default_users_and_groups()

        add_group_permissions(
            "members",
            Member,
            ["view_member"],
        )

        add_group_permissions(
            "admins",
            Member,
            ["view_member", "change_member", "add_member", "delete_member"],
        )

    def test_list_access(self):

        scenarios = (
            ["", 0, 401, "Public can't list members"],
            ["regular_user", 1, 200, "Registered can list self only"],
            ["member", 5, 200, "Member can list all members"],
            ["admin_user", 5, 200, "Admin can list all members"],
        )

        for user, count, status, msg in scenarios:
            if user:
                response = self.assert_status(self.auth_list(user), status, msg)
            else:
                response = self.assert_status(self.public_list(), status, msg)

            if status < 400:
                self.assertEqual(response.body.count, count, msg)

    def test_detail_access(self):

        self.regular_id = Member.objects.get(username="regular_user").id
        self.other_id = Member.objects.get(username="other_user").id

        scenarios = (
            ["", self.regular_id, 401, "Public can't view member detail"],
            ["regular_user", self.other_id, 404, "Regular user can't see other user"],
            ["regular_user", self.regular_id, 200, "Regular user can see self"],
            ["member", self.regular_id, 200, "Member can see other user"],
            ["admin_user", self.regular_id, 200, "Admin can see other user"],
        )

        for user, item, status, msg in scenarios:
            if user:
                response = self.assert_status(self.auth_detail(user, item), status, msg)
                if status == 200:
                    if user == "regular_user":
                        self.assertTrue(response.body.preferences)
                        # self.assertTrue(response.body.permissions)
                    else:
                        self.assertFalse(response.body.preferences)
                        self.assertFalse(response.body.permissions)
            else:
                self.assert_status(self.detail(item), status, msg)

    def test_create_permissions(self):

        scenarios = (
            ["", 401, "Public can't create, no auth"],
            ["regular_user", 403, "Registered can't create, no perm"],
            ["member", 403, "Member can't create, no perm"],
            ["admin_user", 201, "Admin can create"],
        )

        for user, status, msg in scenarios:
            if user:
                self.assert_status(self.auth_create(user, self.template), status, msg)
            else:
                self.assert_status(self.create(self.template), status, msg)

    def test_profile_upload_permissions(self):

        self.regular_id = Member.objects.get(username="regular_user").id
        scenarios = (
            ["", 401, "profile.png", "Public can't upload profile"],
            ["other_user", 403, "profile.png", "Other user can't upload profile"],
            ["member", 403, "profile.png", "Member can't upload profile"],
            ["regular_user", 204, "profile.png", "Can upload own PNG profile"],
            ["regular_user", 204, "profile.jpg", "Can upload own JPG profile"],
            [
                "regular_user",
                400,
                "profile.svg",
                "Can't upload profile with unsupported extension",
            ],
            [
                "regular_user",
                400,
                "bad_profile.png",
                "Can't upload profile with invalid content",
            ],
            ["admin_user", 20, "profile.png", "Admin can upload profile"],
        )

        for user, status, path, msg in scenarios:
            full_path = Path(__file__).parent / "data" / path
            with open(full_path, "rb") as f:
                content = f.read()

            if user:
                self.assert_status(
                    self.auth_put_file(
                        user, self.regular_id, "profile", content, full_path.name
                    ),
                    status,
                    msg,
                )
            else:
                self.assert_status(
                    self.put_file(self.regular_id, "profile", content, full_path.name),
                    status,
                    msg,
                )

    def test_profile_download_permissions(self):

        self.regular_id = Member.objects.get(username="regular_user").id
        scenarios = (
            ["", 401, "Public user can't download profile or thumb"],
            ["regular_user", 200, "Can download own profile and thumb"],
            ["other_user", 404, "Other user can't download profile or thumb"],
            ["member", 200, "Member can download profile and thumb"],
        )
        full_path = Path(__file__).parent / "data/profile.png"
        with open(full_path, "rb") as f:
            content = f.read()
        self.auth_put_file(
            "regular_user", self.regular_id, "profile", content, full_path.name
        )

        for user, status, msg in scenarios:
            if user:
                response = self.assert_status(
                    self.auth_get(user, self.regular_id, "profile"), status, msg
                )
            else:
                self.assert_status(self.get(self.regular_id, "profile"), status, msg)

        if status < 400:
            self.assertTrue(response.body["basename"] == "profile.png")

    def test_update_authenticated(self):
        response = self.auth_create("admin_user", self.template)
        self.assert_201(response)

        user = response.body
        user_update = response.body.model_copy(
            update={
                "identifiers": [
                    response.body.identifiers[0].model_copy(
                        update={"value": "4321-5678-1234-5678"}
                    )
                ]
            }
        )

        response = self.auth_update("admin_user", user.id, user_update)
        self.assert_200(response)

        self.assertTrue(response.body.identifiers[0].value == "4321-5678-1234-5678")

        user_update = response.body.model_copy(
            update={
                "identifiers": [
                    response.body.identifiers[0],
                    MemberIdentifierCreate(id_type="FREEFORM", value="1234"),
                ]
            }
        )

        response = self.auth_update("admin_user", user.id, user_update)
        self.assert_200(response)

        self.assertTrue(len(response.body.identifiers) == 2)
        user_update = response.body.model_copy(update={"identifiers": []})

        response = self.auth_update("admin_user", user.id, user_update)
        self.assert_200(response)

        self.assertTrue(len(response.body.identifiers) == 0)

    def test_delete_permissions(self):

        scenarios = (
            ["", 401, False, "Public can't delete, no auth"],
            ["regular_user", 403, False, "Registered can't delete"],
            ["other_user", 403, True, "Item member can't delete"],
            ["admin_user", 204, False, "Admin can delete"],
        )

        resources = create_members(len(scenarios))
        for idx, (user, status, member, msg) in enumerate(scenarios):
            response = self.auth_create("admin_user", resources[idx])
            if user:
                self.assert_status(
                    self.auth_delete(user, response.body.id), status, msg
                )
            else:
                self.assert_status(self.delete(response.body.id), status, msg)

    def test_self_view(self):

        response = self.assert_status(
            self.auth_raw_get("regular_user", "/api/self/", action="detail"), 200
        )
        self.assertTrue(response.body.username == "regular_user")
