"""
BusinessObject CRUD + 过滤器 API 测试

现有测试主要覆盖 BusinessObject 的 action（advance/reset/timeline/state-logs/...），
这里补齐 list/retrieve/create/update/delete 以及 FilterSet 的关键分支。
"""

from django.contrib.auth import get_user_model
from django.contrib.contenttypes.models import ContentType
from django.test import TestCase
from rest_framework import status
from rest_framework.test import APIClient

from stateflow import models, services

User = get_user_model()


class BusinessObjectCRUDAndFilterAPITestCase(TestCase):
    def setUp(self):
        self.client = APIClient()
        self.user = User.objects.create_user(username="testuser", password="testpass")
        self.client.force_authenticate(user=self.user)

        # 通用状态/流程（3个节点）
        self.state1 = models.State.objects.create(name="状态1")
        self.state2 = models.State.objects.create(name="状态2")
        self.state3 = models.State.objects.create(name="状态3")

        self.process_a = models.Process.objects.create(name="流程A", description="用于测试过滤器A")
        models.ProcessNode.objects.create(process=self.process_a, state=self.state1, order=0)
        models.ProcessNode.objects.create(process=self.process_a, state=self.state2, order=1)
        models.ProcessNode.objects.create(process=self.process_a, state=self.state3, order=2)

        self.process_b = models.Process.objects.create(name="流程B", description="用于测试过滤器B")
        models.ProcessNode.objects.create(process=self.process_b, state=self.state1, order=0)

        self.process_content_type = ContentType.objects.get_for_model(models.Process)
        self.user_content_type = ContentType.objects.get_for_model(User)

    def test_business_object_crud(self):
        """覆盖 create/list/retrieve/patch/delete 的基本 happy path"""
        # create（注意：create serializer 不包含 id，需要从 DB 获取）
        create_payload = {
            "name": "BO-CRUD-1",
            "process": self.process_a.id,
            "description": "测试 CRUD",
            "content_type_str": "stateflow.process",
            "object_id": self.process_a.id,
        }
        resp = self.client.post("/api/v1/stateflow/business-objects/", create_payload, format="json")
        self.assertEqual(resp.status_code, status.HTTP_201_CREATED)

        bo = models.BusinessObject.objects.get(name="BO-CRUD-1")
        self.assertEqual(bo.process_id, self.process_a.id)

        # list
        resp = self.client.get("/api/v1/stateflow/business-objects/?limit=10&offset=0")
        self.assertEqual(resp.status_code, status.HTTP_200_OK)
        self.assertGreaterEqual(resp.data["count"], 1)

        # list 返回字段校验（挑关键字段）
        results = resp.data["results"]
        item = next(x for x in results if x["id"] == bo.id)
        self.assertEqual(item["name"], "BO-CRUD-1")
        self.assertEqual(item["process"], self.process_a.id)
        self.assertEqual(item["process_name"], "流程A")
        self.assertIn("current_state_name", item)
        self.assertIn("overall_status", item)
        self.assertIn("progress_percentage", item)

        # retrieve（详情含 timeline/state_logs 等）
        resp = self.client.get(f"/api/v1/stateflow/business-objects/{bo.id}/")
        self.assertEqual(resp.status_code, status.HTTP_200_OK)
        self.assertEqual(resp.data["id"], bo.id)
        self.assertEqual(resp.data["name"], "BO-CRUD-1")
        self.assertIn("process_detail", resp.data)
        self.assertIn("timeline", resp.data)
        self.assertIn("state_logs", resp.data)
        self.assertEqual(len(resp.data["timeline"]), 3)

        # patch 更新 name
        resp = self.client.patch(
            f"/api/v1/stateflow/business-objects/{bo.id}/",
            {"name": "BO-CRUD-1-RENAMED"},
            format="json",
        )
        self.assertEqual(resp.status_code, status.HTTP_200_OK)
        bo.refresh_from_db()
        self.assertEqual(bo.name, "BO-CRUD-1-RENAMED")

        # delete
        resp = self.client.delete(f"/api/v1/stateflow/business-objects/{bo.id}/")
        self.assertEqual(resp.status_code, status.HTTP_204_NO_CONTENT)
        self.assertFalse(models.BusinessObject.objects.filter(id=bo.id).exists())

    def test_create_business_object_with_content_type_str(self):
        """覆盖 create serializer 的 content_type_str 解析与一致性校验"""
        # 正确：提供 content_type_str + object_id
        resp = self.client.post(
            "/api/v1/stateflow/business-objects/",
            {
                "name": "BO-Linked",
                "process": self.process_a.id,
                "content_type_str": "stateflow.process",
                "object_id": self.process_a.id,
            },
            format="json",
        )
        self.assertEqual(resp.status_code, status.HTTP_201_CREATED)
        bo = models.BusinessObject.objects.get(name="BO-Linked")
        self.assertIsNotNone(bo.content_type)
        self.assertEqual(bo.content_type.app_label, "stateflow")
        self.assertEqual(bo.content_type.model, "process")
        self.assertEqual(bo.object_id, self.process_a.id)

        # 错误：content_type_str 格式不合法
        resp = self.client.post(
            "/api/v1/stateflow/business-objects/",
            {
                "name": "BO-Bad-CT",
                "process": self.process_a.id,
                "content_type_str": "badformat",
                "object_id": self.process_a.id,
            },
            format="json",
        )
        self.assertEqual(resp.status_code, status.HTTP_400_BAD_REQUEST)
        self.assertIn("content_type_str", resp.data)

        # 错误：提供 content_type_str 但缺 object_id
        resp = self.client.post(
            "/api/v1/stateflow/business-objects/",
            {
                "name": "BO-Missing-ObjectId",
                "process": self.process_a.id,
                "content_type_str": "stateflow.process",
            },
            format="json",
        )
        self.assertEqual(resp.status_code, status.HTTP_400_BAD_REQUEST)
        self.assertIn("detail", resp.data)

    def test_business_object_filters(self):
        """覆盖 BusinessObjectFilterSet 的关键过滤条件"""
        bo_in_progress = models.BusinessObject.objects.create(
            name="BO-InProgress-Alpha",
            process=self.process_a,
            description="alpha desc",
            content_type=self.user_content_type,
            object_id=self.user.id,
        )
        bo_completed = models.BusinessObject.objects.create(
            name="BO-Completed-Beta",
            process=self.process_a,
            description="beta desc",
            content_type=self.user_content_type,
            object_id=self.user.id,
        )
        bo_other_process = models.BusinessObject.objects.create(
            name="BO-OtherProcess-Gamma",
            process=self.process_b,
            description="gamma desc",
            content_type=self.user_content_type,
            object_id=self.user.id,
        )
        bo_unlinked = models.BusinessObject.objects.create(
            name="BO-Unlinked-Legacy",
            process=self.process_a,
            description="legacy desc",
            content_type=None,
            object_id=None,
        )

        # 让 bo_completed 完成（该流程无必填参数，直接推进即可）
        services.advance_to_next_state(bo_completed, self.user)
        services.advance_to_next_state(bo_completed, self.user)
        services.advance_to_next_state(bo_completed, self.user)
        self.assertEqual(services.get_overall_status(bo_completed), "completed")

        # name contains
        resp = self.client.get("/api/v1/stateflow/business-objects/?name=Alpha&limit=10&offset=0")
        self.assertEqual(resp.status_code, status.HTTP_200_OK)
        self.assertEqual(resp.data["count"], 1)
        self.assertEqual(resp.data["results"][0]["id"], bo_in_progress.id)

        # process id
        resp = self.client.get(f"/api/v1/stateflow/business-objects/?process={self.process_b.id}&limit=10&offset=0")
        self.assertEqual(resp.status_code, status.HTTP_200_OK)
        self.assertEqual(resp.data["count"], 1)
        self.assertEqual(resp.data["results"][0]["id"], bo_other_process.id)

        # process_name contains
        resp = self.client.get("/api/v1/stateflow/business-objects/?process_name=流程A&limit=10&offset=0")
        self.assertEqual(resp.status_code, status.HTTP_200_OK)
        self.assertEqual(resp.data["count"], 3)
        returned_ids = {x["id"] for x in resp.data["results"]}
        self.assertSetEqual(returned_ids, {bo_in_progress.id, bo_completed.id, bo_unlinked.id})

        # overall_status=completed
        resp = self.client.get("/api/v1/stateflow/business-objects/?overall_status=completed&limit=10&offset=0")
        self.assertEqual(resp.status_code, status.HTTP_200_OK)
        self.assertEqual(resp.data["count"], 1)
        self.assertEqual(resp.data["results"][0]["id"], bo_completed.id)

        # overall_status=in_progress
        resp = self.client.get("/api/v1/stateflow/business-objects/?overall_status=in_progress&limit=10&offset=0")
        self.assertEqual(resp.status_code, status.HTTP_200_OK)
        returned_ids = {x["id"] for x in resp.data["results"]}
        self.assertSetEqual(returned_ids, {bo_in_progress.id, bo_other_process.id, bo_unlinked.id})

        # content_type_str + has_content_object
        ct = ContentType.objects.get_for_model(models.Process)
        bo_linked = models.BusinessObject.objects.create(
            name="BO-Linked-Process",
            process=self.process_a,
            content_type=ct,
            object_id=self.process_a.id,
        )

        resp = self.client.get("/api/v1/stateflow/business-objects/?has_content_object=true&limit=10&offset=0")
        self.assertEqual(resp.status_code, status.HTTP_200_OK)
        returned_ids = {x["id"] for x in resp.data["results"]}
        self.assertSetEqual(
            returned_ids,
            {bo_in_progress.id, bo_completed.id, bo_other_process.id, bo_linked.id},
        )

        resp = self.client.get("/api/v1/stateflow/business-objects/?has_content_object=false&limit=10&offset=0")
        self.assertEqual(resp.status_code, status.HTTP_200_OK)
        returned_ids = {x["id"] for x in resp.data["results"]}
        self.assertSetEqual(returned_ids, {bo_unlinked.id})

        resp = self.client.get("/api/v1/stateflow/business-objects/?content_type_str=stateflow.process&limit=10&offset=0")
        self.assertEqual(resp.status_code, status.HTTP_200_OK)
        returned_ids = {x["id"] for x in resp.data["results"]}
        self.assertSetEqual(returned_ids, {bo_linked.id})

        # 无效 content_type_str：返回空
        resp = self.client.get("/api/v1/stateflow/business-objects/?content_type_str=badformat&limit=10&offset=0")
        self.assertEqual(resp.status_code, status.HTTP_200_OK)
        self.assertEqual(resp.data["count"], 0)

    def test_business_object_filters_by_completed_state_and_param(self):
        """
        新增能力：按“已完成的 state_id + 工艺参数 key/value”反查 BusinessObject 列表

        语义确认：
        - “处于某状态”指已完成该状态节点（存在 StateFlowRecord 且 is_cancelled=False）
        - 参数匹配绑定在同一个 state_log 下的 StateLogParameterRecord.parameters
        """
        ct = self.user_content_type

        bo_match = models.BusinessObject.objects.create(
            name="BO-Match",
            process=self.process_a,
            content_type=ct,
            object_id=self.user.id,
        )
        services.advance_to_next_state(bo_match, self.user, **{"设计师名称": "AAA"})

        bo_other_value = models.BusinessObject.objects.create(
            name="BO-OtherValue",
            process=self.process_a,
            content_type=ct,
            object_id=self.user.id,
        )
        services.advance_to_next_state(bo_other_value, self.user, **{"设计师名称": "BBB"})

        bo_cancelled = models.BusinessObject.objects.create(
            name="BO-Cancelled",
            process=self.process_a,
            content_type=ct,
            object_id=self.user.id,
        )
        services.advance_to_next_state(bo_cancelled, self.user, **{"设计师名称": "AAA"})
        services.step_back_one_state(bo_cancelled, self.user)

        resp = self.client.get(
            "/api/v1/stateflow/business-objects/",
            data={
                "content_type_str": "auth.user",
                "state_id": self.state1.id,
                "param_key": "设计师名称",
                "param_value": "AAA",
                "limit": 50,
                "offset": 0,
            },
        )
        self.assertEqual(resp.status_code, status.HTTP_200_OK)
        returned_ids = {x["id"] for x in resp.data["results"]}
        self.assertSetEqual(returned_ids, {bo_match.id})


class AuthenticationGuardAPITestCase(TestCase):
    def test_stateflow_endpoints_require_authentication(self):
        """默认权限 IsAuthenticated：未登录应 401"""
        client = APIClient()
        resp = client.get("/api/v1/stateflow/states/?limit=1&offset=0")
        self.assertEqual(resp.status_code, status.HTTP_401_UNAUTHORIZED)


