from apps.modules import services as modules
from apps.personnel.models import Contract
from tests.base import CoreTestCase

URL = "/api/v1/personnel/"


class PersonnelTests(CoreTestCase):
    def setUp(self):
        super().setUp()
        modules.enable_module(actor=self.root, organization=self.org_a, module_code="personnel")
        self.m_emp = self.membership(self.employee_a, self.org_a)
        self.m_mgr = self.membership(self.manager_a, self.org_a)

    def test_employee_sees_only_own_record(self):
        r = self.api(self.employee_a, self.org_a).get(URL)
        self.assertEqual([p["membership_id"] for p in r.data["results"]], [self.m_emp.pk])
        self.assertEqual(self.api(self.employee_a, self.org_a).get(f"{URL}{self.m_mgr.pk}/").status_code, 404)

    def test_manager_sees_everyone_in_org_only(self):
        ids = {p["membership_id"] for p in self.api(self.manager_a, self.org_a).get(URL).data["results"]}
        self.assertIn(self.m_emp.pk, ids)
        self.assertNotIn(self.membership(self.employee_b, self.org_b).pk, ids)

    def test_only_manager_edits_profile(self):
        body = {"job_title": "Dev", "department": "R&D"}
        self.assertEqual(self.api(self.employee_a, self.org_a).patch(f"{URL}{self.m_emp.pk}/", body, format="json").status_code, 403)
        r = self.api(self.manager_a, self.org_a).patch(f"{URL}{self.m_emp.pk}/", body, format="json")
        self.assertEqual(r.status_code, 200)
        self.assertEqual(r.data["job_title"], "Dev")
        own = self.api(self.employee_a, self.org_a).get(f"{URL}{self.m_emp.pk}/")
        self.assertEqual(own.data["department"], "R&D")

    def test_profile_validation(self):
        c = self.api(self.manager_a, self.org_a)
        self.assertEqual(c.patch(f"{URL}{self.m_emp.pk}/", {"employment_type": "x"}, format="json").status_code, 400)
        bad = {"hire_date": "2024-05-01", "end_date": "2024-01-01"}
        self.assertEqual(c.patch(f"{URL}{self.m_emp.pk}/", bad, format="json").status_code, 400)

    def test_cannot_edit_membership_of_another_org(self):
        other = self.membership(self.employee_b, self.org_b)
        r = self.api(self.manager_a, self.org_a).patch(f"{URL}{other.pk}/", {"job_title": "x"}, format="json")
        self.assertEqual(r.status_code, 404)

    def test_contracts_are_manager_only_and_tenant_bound(self):
        body = {"title": "c1", "start_date": "2024-01-01", "pay_amount": 1000}
        self.assertEqual(self.api(self.employee_a, self.org_a).get(f"{URL}{self.m_emp.pk}/contracts/").status_code, 403)
        self.assertEqual(self.api(self.employee_a, self.org_a).post(f"{URL}{self.m_emp.pk}/contracts/", body, format="json").status_code, 403)
        mgr = self.api(self.manager_a, self.org_a)
        r = mgr.post(f"{URL}{self.m_emp.pk}/contracts/", body, format="json")
        self.assertEqual(r.status_code, 201)
        self.assertEqual(len(mgr.get(f"{URL}{self.m_emp.pk}/contracts/").data), 1)
        modules.enable_module(actor=self.root, organization=self.org_b, module_code="personnel")
        self.assertEqual(self.api(self.manager_b, self.org_b).patch(f"{URL}contracts/{r.data['id']}/", {"status": "ended"}, format="json").status_code, 404)
        self.assertEqual(mgr.patch(f"{URL}contracts/{r.data['id']}/", {"status": "ended"}, format="json").data["status"], "ended")
        self.assertEqual(mgr.delete(f"{URL}contracts/{r.data['id']}/").status_code, 204)
        self.assertEqual(Contract.objects.count(), 0)

    def test_contract_validation(self):
        mgr = self.api(self.manager_a, self.org_a)
        url = f"{URL}{self.m_emp.pk}/contracts/"
        self.assertEqual(mgr.post(url, {"title": "x", "start_date": "2024-02-01", "end_date": "2024-01-01"}, format="json").status_code, 400)
        self.assertEqual(mgr.post(url, {"title": "x", "start_date": "2024-02-01", "pay_amount": -5}, format="json").status_code, 400)

    def test_remove_from_personnel_keeps_account(self):
        mgr = self.api(self.manager_a, self.org_a)
        self.assertEqual(self.api(self.employee_a, self.org_a).delete(f"{URL}{self.m_emp.pk}/").status_code, 403)
        self.assertEqual(mgr.delete(f"{URL}{self.m_emp.pk}/").status_code, 204)
        self.m_emp.refresh_from_db()
        self.assertEqual(self.m_emp.status, "left")
        self.employee_a.refresh_from_db()
        self.assertTrue(self.employee_a.is_active)
        ids = {p["membership_id"] for p in mgr.get(URL).data["results"]}
        self.assertNotIn(self.m_emp.pk, ids)
        left = {p["membership_id"] for p in mgr.get(URL, {"status": "left"}).data["results"]}
        self.assertIn(self.m_emp.pk, left)

    def test_cannot_remove_self_or_other_org(self):
        mgr = self.api(self.manager_a, self.org_a)
        self.assertEqual(mgr.delete(f"{URL}{self.m_mgr.pk}/").status_code, 400)
        other = self.membership(self.employee_b, self.org_b)
        self.assertEqual(mgr.delete(f"{URL}{other.pk}/").status_code, 404)

    def test_module_disabled(self):
        modules.disable_module(actor=self.root, organization=self.org_b, module_code="personnel")
        self.assertEqual(self.api(self.manager_b, self.org_b).get(URL).status_code, 403)
        modules.disable_module(actor=self.root, organization=self.org_a, module_code="personnel")
        self.assertEqual(self.api(self.manager_a, self.org_a).get(URL).status_code, 403)
