"""bootstrap_core / bootstrap_platform: moving existing accounts into the new structure safely."""
from django.core.management import call_command
from django.core.management.base import CommandError
from django.test import TestCase, override_settings
from rest_framework.test import APIClient

from apps.access.models import MembershipRole, Role
from apps.modules.services import enabled_module_codes
from apps.organizations.models import Organization, OrganizationMembership
from infrastructure.models import User
from tests.base import FAST_HASHERS


@override_settings(PASSWORD_HASHERS=FAST_HASHERS, ROOT_URLCONF="tests.urls")
class BootstrapTests(TestCase):
    def setUp(self):
        self.admin = User.objects.create_user(personnel_code="900", password="x-Legacy-1", is_admin=True)
        self.staff = User.objects.create_user(personnel_code="901", password="x-Legacy-1")

    def run_bootstrap(self):
        call_command("bootstrap_core", verbosity=0)

    def test_existing_accounts_land_in_the_default_organization(self):
        self.run_bootstrap()
        default = Organization.objects.get(slug="default")
        self.assertEqual(default.kind, "customer")
        self.assertEqual(Organization.objects.filter(kind="platform").count(), 1)

        roles = {
            m.user.personnel_code: set(m.membership_roles.values_list("role__code", flat=True))
            for m in OrganizationMembership.objects.filter(organization=default).select_related("user")
        }
        self.assertEqual(roles, {"900": {"org_manager"}, "901": {"employee"}})
        self.assertEqual(
            OrganizationMembership.objects.get(user=self.admin, organization=default).personnel_code, "900"
        )

    def test_attendance_is_enabled_for_the_default_organization(self):
        self.run_bootstrap()
        self.assertEqual(enabled_module_codes(Organization.objects.get(slug="default")), {"core", "attendance"})

    def test_it_is_idempotent(self):
        self.run_bootstrap()
        counts = (
            Organization.objects.count(), OrganizationMembership.objects.count(),
            MembershipRole.objects.count(), Role.objects.count(),
        )
        self.run_bootstrap()
        self.assertEqual(
            counts,
            (Organization.objects.count(), OrganizationMembership.objects.count(),
             MembershipRole.objects.count(), Role.objects.count()),
        )

    def test_it_does_not_overwrite_manual_changes(self):
        self.run_bootstrap()
        membership = OrganizationMembership.objects.get(user=self.staff, organization__slug="default")
        MembershipRole.objects.filter(membership=membership).delete()
        self.run_bootstrap()  # membership already exists -> left alone
        self.assertFalse(MembershipRole.objects.filter(membership=membership).exists())

    def test_nobody_becomes_platform_staff_implicitly(self):
        self.run_bootstrap()
        platform = Organization.objects.get(kind="platform")
        self.assertFalse(OrganizationMembership.objects.filter(organization=platform).exists())

    def test_super_admin_must_be_appointed_explicitly(self):
        with self.assertRaises(CommandError):
            call_command("bootstrap_platform", username="900", verbosity=0)  # before bootstrap_core
        self.run_bootstrap()
        with self.assertRaises(CommandError):
            call_command("bootstrap_platform", username="nobody", verbosity=0)
        call_command("bootstrap_platform", username="900", verbosity=0)

        client = APIClient()
        client.force_authenticate(self.admin)
        me = client.get("/api/v1/me/").data
        self.assertEqual({o["slug"] for o in me["organizations"]}, {"noteam", "default"})
        self.assertEqual(client.get("/api/v1/organizations/").status_code, 200)

        # regression: 900 is org_manager of 'default' AND super admin - platform powers must still work there
        default = Organization.objects.get(slug="default")
        toggled = client.post(f"/api/v1/organizations/{default.pk}/modules/attendance/disable/")
        self.assertEqual(toggled.status_code, 200, toggled.data)
        self.assertEqual(client.post(f"/api/v1/organizations/{default.pk}/modules/attendance/enable/").status_code, 200)

        # a regular legacy account is still not platform staff
        regular = APIClient()
        regular.force_authenticate(self.staff)
        self.assertEqual(regular.get("/api/v1/organizations/").status_code, 403)

    def test_create_admin_makes_an_org_manager_but_never_platform_staff(self):
        with self.assertRaises(CommandError):
            call_command("create_admin", code="9000", password="x-Admin-1", verbosity=0)  # needs bootstrap_core first
        self.run_bootstrap()
        call_command("create_admin", code="9000", password="x-Admin-1", name="New Admin", verbosity=0)
        with self.assertRaises(CommandError):
            call_command("create_admin", code="9000", password="x-Admin-1", verbosity=0)  # duplicate

        admin = User.objects.get(personnel_code="9000")
        client = APIClient()
        client.force_authenticate(admin)
        client.credentials(HTTP_X_ORGANIZATION="default")
        context = client.get("/api/v1/me/").data["context"]
        self.assertIn("members.create", context["permissions"])
        self.assertFalse(context["via_platform"])
        self.assertEqual(client.get("/api/v1/organizations/").status_code, 403)

    def test_legacy_admin_flag_alone_grants_nothing_in_the_new_system(self):
        self.run_bootstrap()
        client = APIClient()
        client.force_authenticate(self.admin)
        client.credentials(HTTP_X_ORGANIZATION="default")
        context = client.get("/api/v1/me/").data["context"]
        self.assertIn("members.view", context["permissions"])  # via org_manager role, not via is_admin
        self.assertNotIn("organizations.view", context["permissions"])
        self.assertEqual(client.get("/api/v1/organizations/").status_code, 403)
