from django.contrib.auth import get_user_model
from django.contrib.auth.models import Permission
from django.test import TestCase
from rest_framework.test import APIClient

from basic_info import models as basic_models
from mission import models as mission_models


def grant_permission(user, codename):
    permission = Permission.objects.get(codename=codename)
    user.user_permissions.add(permission)


class MissionV2APITest(TestCase):
    def setUp(self):
        self.client = APIClient()
        self.merchant = basic_models.Merchant.objects.create(
            name="任务商户",
            type=basic_models.MerchantTypeEnum.STORE,
        )
        self.other_merchant = basic_models.Merchant.objects.create(
            name="其他商户",
            type=basic_models.MerchantTypeEnum.STORE,
        )
        self.user = get_user_model().objects.create_user(username="mission-user", password="pass12345")
        self.employee = basic_models.Employee.objects.create(
            merchant=self.merchant,
            sys_user=self.user,
            name="任务员工",
        )
        self.default_category = mission_models.MissionCategory.objects.create(
            merchant=self.merchant,
            name="通用",
        )
        self.followup_category = mission_models.MissionCategory.objects.create(
            merchant=self.merchant,
            name="跟进",
        )
        self.other_category = mission_models.MissionCategory.objects.create(
            merchant=self.other_merchant,
            name="通用",
        )
        self.participant = basic_models.Employee.objects.create(
            merchant=self.merchant,
            name="参与者",
        )
        self.other_employee = basic_models.Employee.objects.create(
            merchant=self.other_merchant,
            name="其他员工",
        )
        self.client.force_authenticate(user=self.user)

    def test_create_mission_uses_default_status_and_current_employee(self):
        resp = self.client.post(
            "/api/v2/missions/",
            {
                "description": "跟进客户问题",
                "participant_ids": [self.participant.id],
            },
            format="json",
        )

        self.assertEqual(resp.status_code, 201)
        mission = mission_models.Mission.objects.get(id=resp.data["id"])
        self.assertEqual(mission.merchant, self.merchant)
        self.assertEqual(mission.creator, self.employee)
        self.assertEqual(mission.category, self.default_category)
        self.assertFalse(mission.is_urgent)
        self.assertFalse(mission.is_completed)
        self.assertFalse(mission.is_cancelled)
        self.assertFalse(mission.notify_if_unreplied)
        self.assertEqual(mission.unreplied_notify_max_count, 5)
        self.assertEqual(resp.data["category"], self.default_category.id)
        self.assertEqual(resp.data["category_name"], "通用")
        self.assertEqual(list(mission.participants.values_list("employee_id", flat=True)), [self.participant.id])

    def test_create_mission_supports_unreplied_notification_fields(self):
        resp = self.client.post(
            "/api/v2/missions/",
            {
                "description": "需要未回复提醒",
                "notify_if_unreplied": True,
                "unreplied_notify_interval_minutes": 30,
            },
            format="json",
        )

        self.assertEqual(resp.status_code, 201)
        self.assertTrue(resp.data["notify_if_unreplied"])
        self.assertEqual(resp.data["unreplied_notify_interval_minutes"], 30)
        self.assertEqual(resp.data["unreplied_notify_max_count"], 5)
        self.assertEqual(resp.data["unreplied_notify_sent_count"], 0)

    def test_create_mission_rejects_missing_interval_when_unreplied_notification_enabled(self):
        resp = self.client.post(
            "/api/v2/missions/",
            {
                "description": "缺少提醒间隔",
                "notify_if_unreplied": True,
            },
            format="json",
        )

        self.assertEqual(resp.status_code, 400)
        self.assertIn("unreplied_notify_interval_minutes", resp.data)

    def test_create_mission_rejects_status_fields(self):
        resp = self.client.post(
            "/api/v2/missions/",
            {
                "description": "非法状态",
                "is_completed": True,
            },
            format="json",
        )

        self.assertEqual(resp.status_code, 400)
        self.assertIn("is_completed", resp.data)

    def test_create_mission_rejects_cross_merchant_category(self):
        resp = self.client.post(
            "/api/v2/missions/",
            {
                "description": "非法分类",
                "category": self.other_category.id,
            },
            format="json",
        )

        self.assertEqual(resp.status_code, 400)
        self.assertIn("category", resp.data)

    def test_patch_mission_rejects_status_fields(self):
        mission = self._create_mission()

        resp = self.client.patch(
            f"/api/v2/missions/{mission.id}/",
            {"is_cancelled": True},
            format="json",
        )

        self.assertEqual(resp.status_code, 400)
        mission.refresh_from_db()
        self.assertFalse(mission.is_cancelled)

    def test_patch_mission_rejects_is_urgent(self):
        mission = self._create_mission()

        resp = self.client.patch(
            f"/api/v2/missions/{mission.id}/",
            {"is_urgent": True},
            format="json",
        )

        self.assertEqual(resp.status_code, 400)
        mission.refresh_from_db()
        self.assertFalse(mission.is_urgent)

    def test_patch_mission_updates_normal_fields_and_participants(self):
        mission = self._create_mission()

        resp = self.client.patch(
            f"/api/v2/missions/{mission.id}/",
            {
                "description": "更新后的任务",
                "category": self.followup_category.id,
                "participant_ids": [self.participant.id],
            },
            format="json",
        )

        self.assertEqual(resp.status_code, 200)
        mission.refresh_from_db()
        self.assertEqual(mission.description, "更新后的任务")
        self.assertEqual(mission.category, self.followup_category)
        self.assertEqual(resp.data["category"], self.followup_category.id)
        self.assertEqual(resp.data["category_name"], "跟进")
        self.assertEqual(list(mission.participants.values_list("employee_id", flat=True)), [self.participant.id])

    def test_patch_mission_updates_unreplied_notification_fields(self):
        mission = self._create_mission()

        resp = self.client.patch(
            f"/api/v2/missions/{mission.id}/",
            {
                "notify_if_unreplied": True,
                "unreplied_notify_interval_minutes": 12,
                "unreplied_notify_max_count": 7,
            },
            format="json",
        )

        self.assertEqual(resp.status_code, 200)
        mission.refresh_from_db()
        self.assertTrue(mission.notify_if_unreplied)
        self.assertEqual(mission.unreplied_notify_interval_minutes, 12)
        self.assertEqual(mission.unreplied_notify_max_count, 7)

    def test_list_only_returns_current_merchant_missions(self):
        visible = self._create_mission(description="可见任务")
        mission_models.Mission.objects.create(
            merchant=self.other_merchant,
            category=self.other_category,
            creator=self.other_employee,
            description="不可见任务",
        )

        resp = self.client.get("/api/v2/missions/")

        self.assertEqual(resp.status_code, 200)
        self.assertEqual([item["id"] for item in resp.data], [visible.id])

    def test_delete_mission_is_not_allowed(self):
        mission = self._create_mission()

        resp = self.client.delete(f"/api/v2/missions/{mission.id}/")

        self.assertEqual(resp.status_code, 405)

    def test_reopen_mission_requires_permission(self):
        mission = self._create_mission()

        reply_resp = self.client.post(
            f"/api/v2/missions/{mission.id}/replies/",
            {"content": "已处理", "ends_task": True},
            format="json",
        )
        self.assertEqual(reply_resp.status_code, 201)
        mission.refresh_from_db()
        self.assertTrue(mission.is_completed)

        reopen_resp = self.client.post(f"/api/v2/missions/{mission.id}/reopen/", {}, format="json")
        self.assertEqual(reopen_resp.status_code, 403)

    def test_create_ending_reply_and_reopen_mission(self):
        grant_permission(self.user, "reopen_mission")
        mission = self._create_mission()

        reply_resp = self.client.post(
            f"/api/v2/missions/{mission.id}/replies/",
            {"content": "已处理", "ends_task": True},
            format="json",
        )
        self.assertEqual(reply_resp.status_code, 201)
        mission.refresh_from_db()
        self.assertTrue(mission.is_completed)

        reopen_resp = self.client.post(f"/api/v2/missions/{mission.id}/reopen/", {}, format="json")
        self.assertEqual(reopen_resp.status_code, 200)
        mission.refresh_from_db()
        reply = mission_models.MissionReply.objects.get(id=reply_resp.data["id"])
        self.assertFalse(mission.is_completed)
        self.assertFalse(reply.ends_task)
        self.assertTrue(reply.is_rejected)
        self.assertEqual(reply.rejected_by, self.employee)

    def test_reject_reply_requires_permission(self):
        mission = self._create_mission()
        reply = mission_models.MissionReply.objects.create(
            merchant=self.merchant,
            mission=mission,
            responder=self.employee,
            content="结束",
            ends_task=True,
        )
        mission.is_completed = True
        mission.save(update_fields=["is_completed", "updated_at"])

        resp = self.client.post(f"/api/v2/mission-replies/{reply.id}/reject/", {}, format="json")
        self.assertEqual(resp.status_code, 403)

    def test_reject_reply_api_reopens_mission_when_rejecting_ending_reply(self):
        grant_permission(self.user, "reject_mission_reply")
        mission = self._create_mission()
        reply = mission_models.MissionReply.objects.create(
            merchant=self.merchant,
            mission=mission,
            responder=self.employee,
            content="结束",
            ends_task=True,
        )
        mission.is_completed = True
        mission.save(update_fields=["is_completed", "updated_at"])

        resp = self.client.post(f"/api/v2/mission-replies/{reply.id}/reject/", {}, format="json")
        self.assertEqual(resp.status_code, 200)
        mission.refresh_from_db()
        reply.refresh_from_db()
        self.assertFalse(mission.is_completed)
        self.assertTrue(reply.is_rejected)
        self.assertEqual(reply.rejected_by, self.employee)

    def test_cancel_mission_api_records_cancelled_by(self):
        mission = self._create_mission()

        resp = self.client.post(f"/api/v2/missions/{mission.id}/cancel/", {}, format="json")

        self.assertEqual(resp.status_code, 200)
        mission.refresh_from_db()
        self.assertTrue(mission.is_cancelled)
        self.assertEqual(mission.cancelled_by, self.employee)
        self.assertIsNotNone(mission.cancelled_at)

    def test_set_urgent_api_updates_urgent_status(self):
        mission = self._create_mission()

        resp = self.client.post(
            f"/api/v2/missions/{mission.id}/set-urgent/",
            {"is_urgent": True},
            format="json",
        )

        self.assertEqual(resp.status_code, 200)
        mission.refresh_from_db()
        self.assertTrue(mission.is_urgent)

    def test_cross_merchant_mission_detail_returns_404(self):
        mission = mission_models.Mission.objects.create(
            merchant=self.other_merchant,
            category=self.other_category,
            creator=self.other_employee,
            description="其他商户任务",
        )

        resp = self.client.get(f"/api/v2/missions/{mission.id}/")

        self.assertEqual(resp.status_code, 404)

    def test_cross_merchant_participant_is_rejected(self):
        resp = self.client.post(
            "/api/v2/missions/",
            {
                "description": "跨商户参与者",
                "participant_ids": [self.other_employee.id],
            },
            format="json",
        )

        self.assertEqual(resp.status_code, 400)

    def test_mission_category_crud(self):
        list_resp = self.client.get("/api/v2/mission-categories/")
        self.assertEqual(list_resp.status_code, 200)
        self.assertEqual(
            [item["name"] for item in list_resp.data],
            ["通用", "跟进"],
        )

        create_resp = self.client.post(
            "/api/v2/mission-categories/",
            {"name": "售后"},
            format="json",
        )
        self.assertEqual(create_resp.status_code, 201)
        category_id = create_resp.data["id"]

        detail_resp = self.client.get(f"/api/v2/mission-categories/{category_id}/")
        self.assertEqual(detail_resp.status_code, 200)
        self.assertEqual(detail_resp.data["name"], "售后")

        patch_resp = self.client.patch(
            f"/api/v2/mission-categories/{category_id}/",
            {"name": "售后跟进"},
            format="json",
        )
        self.assertEqual(patch_resp.status_code, 200)
        self.assertEqual(patch_resp.data["name"], "售后跟进")

        delete_resp = self.client.delete(f"/api/v2/mission-categories/{category_id}/")
        self.assertEqual(delete_resp.status_code, 204)

    def test_delete_used_mission_category_is_rejected(self):
        mission = self._create_mission()

        resp = self.client.delete(f"/api/v2/mission-categories/{mission.category_id}/")

        self.assertEqual(resp.status_code, 400)

    def _create_mission(self, description="测试任务"):
        return mission_models.Mission.objects.create(
            merchant=self.merchant,
            category=self.default_category,
            creator=self.employee,
            description=description,
        )
