from pathlib import Path

from ichec_django_core.client import MemberDetail, FormFieldCreate

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

from marinerg_test_access.client import AccessCallUpdate
from marinerg_test_access.client.resources import create_access_calls
from marinerg_test_access.models import AccessCall
from marinerg_test_access.test import setup_default_access_calls


class TestAccessCallView(AuthAPITestCase):

    def _get_review_board(self) -> list[MemberDetail]:
        return [
            self.get_member_detail(n).body
            for n in ["coordinator", "chair", "reviewer1", "reviewer2", "reviewer3"]
        ]

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

        setup_default_users_and_groups()
        add_group_permissions(
            "admins",
            AccessCall,
            [
                "view_accesscall",
                "change_accesscall",
                "add_accesscall",
                "delete_accesscall",
            ],
        )
        add_group_permissions("members", AccessCall, ["list_accesscall"])

        self.internal, self.public, self.inactive = setup_default_access_calls()
        self.template = create_access_calls(1, members=self._get_review_board())[0]

    def get_template(
        self, coordinator: str = "coordinator", chair: str = "chair"
    ) -> dict:
        return self.template.model_copy(
            update={
                "coordinator": self.get_member_url(coordinator),
                "board_chair": self.get_member_url(chair),
            }
        )

    def test_list_access(self):

        scenarios = (
            ["", 1, 200, "Public can list public items"],
            ["regular_user", 1, 200, "Registered can list public items"],
            ["member", 2, 200, "Member can list all items"],
            ["coordinator", 2, 200, "Coordinator can list item"],
            ["reviewer1", 2, 200, "Reviewer can list item"],
            ["chair", 2, 200, "Chair can list item"],
        )
        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)

            self.assertEqual(response.body.count, count, msg)

    def test_detail_access(self):

        public = self.public.id
        internal = self.internal.id
        scenarios = (
            ["", public, "public", 200, "Public can view public item"],
            ["", internal, "public", 404, "Public can't view internal item"],
            ["regular_user", public, "public", 200, "Registered can view public item"],
            [
                "regular_user",
                internal,
                "public",
                404,
                "Registered can't view internal item",
            ],
            ["member", public, "public", 200, "Member can view public item"],
            ["member", internal, "public", 200, "Member can view internal item"],
            ["coordinator", internal, "", 200, "Coordinator can view internal item"],
            ["chair", internal, "", 200, "Chair can view internal item"],
            ["reviewer1", internal, "reviewer", 200, "Reviewer can view internal item"],
        )

        for user, item, role, status, msg in scenarios:
            if user:
                self.assert_status(self.auth_detail(user, item, role), status, msg)
            else:
                self.assert_status(self.detail(item, role), 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.get_template()), status, msg
                )
            else:
                self.assert_status(self.create(self.get_template()), status, msg)

    def test_update_permissions(self):
        created = self.assert_201(
            self.auth_create("admin_user", self.get_template())
        ).body

        updated = AccessCallUpdate.from_detail(created)

        new_field = FormFieldCreate(
            label="Requires More Support",
            key="requires_more_support",
            description="Do you require more facility support?",
            field_type="BOOLEAN",
            default="false",
        )
        updated_group = updated.form.groups[0].model_copy(
            update={"fields": updated.form.groups[0].fields + [new_field]}
        )

        updated = updated.model_copy(
            update={
                "description": "Modified description of my access call",
                "form": created.form.model_copy(update={"groups": [updated_group]}),
            }
        )

        updated = self.assert_200(self.auth_update("admin_user", created.id, updated))

        self.assertTrue(
            updated.body.description == "Modified description of my access call"
        )
        self.assertTrue(len(updated.body.form.groups[0].fields) == 3)

    def test_upload_permissions(self):

        created = self.assert_201(
            self.auth_create("admin_user", self.get_template())
        ).body

        scenarios = (
            ["", 401, "Sample_doc.doc", "Public can't upload"],
            ["regular_user", 403, "Sample_doc.doc", "Registered can't upload"],
            ["member", 403, "Sample_doc.doc", "Member can't upload"],
            ["coordinator", 204, "Sample_doc.doc", "Coordinator can upload"],
            ["chair", 403, "Sample_doc.doc", "Chair can't upload"],
            ["reviewer1", 403, "Sample_doc.doc", "Reviewer can't upload"],
            ["admin_user", 204, "Sample_doc.doc", "Admin can upload"],
        )

        fields = [
            "review_template",
            "facility_review_template",
            "decision_template",
            "test_report_template",
        ]

        for field in fields:
            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, created.id, field, content, full_path.name
                        ),
                        status,
                        msg,
                    )
                else:
                    self.assert_status(
                        self.put_file(created.id, field, content, full_path.name),
                        status,
                        msg,
                    )

    def _upload_file(self, user, resource, field):
        full_path = Path(__file__).parent / "data/Sample_doc.doc"
        with open(full_path, "rb") as f:
            content = f.read()
        self.assert_204(
            self.auth_put_file(user, resource, field, content, full_path.name)
        )

    def test_download_permissions(self):

        reviewer_fields = ["review_template"]
        facility_member_fields = ["facility_review_template", "test_report_template"]
        chair_fields = [
            "review_template",
            "facility_review_template",
            "decision_template",
            "test_report_template",
        ]
        coordinator_fields = [
            "review_template",
            "facility_review_template",
            "decision_template",
            "test_report_template",
        ]
        all_fields = [
            "review_template",
            "facility_review_template",
            "decision_template",
            "test_report_template",
        ]

        scenarios = (
            ["", self.public.id, [], "Public user can't download"],
            ["regular_user", self.public.id, [], "Registered can't download"],
            ["member", self.public.id, [], "Member can't download"],
            [
                "coordinator",
                self.public.id,
                coordinator_fields,
                "Coordinator can download",
            ],
            ["chair", self.public.id, chair_fields, "Chair can download"],
            ["reviewer1", self.public.id, reviewer_fields, "Reviewer can download"],
            ["admin_user", self.public.id, all_fields, "Admin can download"],
        )

        for field in all_fields:
            self._upload_file("coordinator", self.public.id, field)

            for user, resource, expected_fields, msg in scenarios:
                if field in expected_fields:
                    status = 200
                else:
                    status = 403
                if user:
                    response = self.assert_status(
                        self.auth_get(user, resource, field), status, msg
                    )
                else:
                    self.assert_status(self.get(resource, field), 401, msg)

            if status < 400:
                self.assertTrue(response.body["basename"] == f"{field}.doc")
