from apps.modules.models import ModuleRequest
from apps.modules.services import enabled_module_codes
from tests.base import CoreTestCase


class ModuleRequestTests(CoreTestCase):
    def request_url(self, org, code):
        return f"/api/v1/organizations/{org.pk}/modules/{code}/request/"

    def test_manager_requests_and_super_admin_approves(self):
        r = self.api(self.manager_a, self.org_a).post(self.request_url(self.org_a, "crm"))
        self.assertEqual(r.status_code, 201, r.data)
        crm = next(m for m in r.data if m["code"] == "crm")
        self.assertEqual(crm["request_status"], "pending")
        self.assertNotIn("crm", enabled_module_codes(self.org_a))  # a request does not enable anything

        pending = self.api(self.root).get("/api/v1/platform/module-requests/").data
        self.assertEqual([(p["organization"]["slug"], p["module"]["code"]) for p in pending], [("org-a", "crm")])

        done = self.api(self.root).post(f"/api/v1/platform/module-requests/{pending[0]['id']}/approve/")
        self.assertEqual(done.status_code, 200, done.data)
        self.assertIn("crm", enabled_module_codes(self.org_a))
        self.assertEqual(self.api(self.root).get("/api/v1/platform/module-requests/").data, [])

    def test_history_lists_decided_requests_with_who_decided(self):
        client = self.api(self.manager_a, self.org_a)
        client.post(self.request_url(self.org_a, "crm"))
        client.post(self.request_url(self.org_a, "finance"))
        client.post(self.request_url(self.org_a, "payroll"))
        rows = {r.module_id: r for r in ModuleRequest.objects.all()}
        self.api(self.root).post(f"/api/v1/platform/module-requests/{rows['crm'].pk}/approve/")
        self.api(self.root).post(f"/api/v1/platform/module-requests/{rows['finance'].pk}/reject/", {"note": "later"}, format="json")
        history = self.api(self.root).get("/api/v1/platform/module-requests/", {"status": "decided"}).data
        self.assertEqual({(h["module"]["code"], h["status"]) for h in history}, {("crm", "approved"), ("finance", "rejected")})
        self.assertTrue(all(h["decided_by"]["username"] == "root" and h["decided_at"] for h in history))
        pending = self.api(self.root).get("/api/v1/platform/module-requests/").data
        self.assertEqual([p["module"]["code"] for p in pending], ["payroll"])

    def test_rejection_needs_a_reason_and_keeps_module_off(self):
        self.api(self.manager_a, self.org_a).post(self.request_url(self.org_a, "crm"))
        row = ModuleRequest.objects.get()
        url = f"/api/v1/platform/module-requests/{row.pk}/reject/"
        self.assertEqual(self.api(self.root).post(url).status_code, 400)
        self.assertEqual(self.api(self.root).post(url, {"note": "not now"}, format="json").status_code, 200)
        self.assertNotIn("crm", enabled_module_codes(self.org_a))
        states = self.api(self.manager_a, self.org_a).get(f"/api/v1/organizations/{self.org_a.pk}/modules/").data
        crm = next(m for m in states if m["code"] == "crm")
        self.assertEqual((crm["request_status"], crm["request_note"]), ("rejected", "not now"))
        # after a rejection the manager may ask again
        self.assertEqual(self.api(self.manager_a, self.org_a).post(self.request_url(self.org_a, "crm")).status_code, 201)

    def test_duplicate_and_enabled_requests_are_refused(self):
        client = self.api(self.manager_a, self.org_a)
        self.assertEqual(client.post(self.request_url(self.org_a, "crm")).status_code, 201)
        self.assertEqual(client.post(self.request_url(self.org_a, "crm")).status_code, 400)
        self.assertEqual(client.post(self.request_url(self.org_a, "attendance")).status_code, 400)
        self.assertEqual(client.post(self.request_url(self.org_a, "inventory")).status_code, 400)  # planned

    def test_only_managers_request_and_only_super_admin_decides(self):
        self.assertEqual(self.api(self.employee_a, self.org_a).post(self.request_url(self.org_a, "crm")).status_code, 403)
        self.assertEqual(self.api(self.manager_b, self.org_b).post(self.request_url(self.org_a, "crm")).status_code, 403)
        self.api(self.manager_a, self.org_a).post(self.request_url(self.org_a, "crm"))
        row = ModuleRequest.objects.get()
        self.assertEqual(self.api(self.manager_a, self.org_a).post(f"/api/v1/platform/module-requests/{row.pk}/approve/").status_code, 403)
        self.assertEqual(self.api(self.manager_a).get("/api/v1/platform/module-requests/").status_code, 403)
        self.assertNotIn("crm", enabled_module_codes(self.org_a))

    def test_dependency_blocks_approval(self):
        self.api(self.manager_a, self.org_a).post(self.request_url(self.org_a, "invoicing"))  # needs crm
        row = ModuleRequest.objects.get()
        r = self.api(self.root).post(f"/api/v1/platform/module-requests/{row.pk}/approve/")
        self.assertEqual(r.status_code, 400)
        row.refresh_from_db()
        self.assertEqual(row.status, "pending")

    def test_menu_entry_only_for_the_platform(self):
        def keys(user, org):
            return {i["key"] for i in self.api(user, org).get("/api/v1/me/").data["context"]["navigation"]}

        self.assertIn("module_requests", keys(self.root, self.platform))
        self.assertNotIn("module_requests", keys(self.manager_a, self.org_a))
