import os
import unittest
from unittest.mock import AsyncMock

from core.pydantic_ai_agent import CustomerServiceAgent, CustomerMessage
from db.customer_db import db


class RegressionPipelineTest(unittest.IsolatedAsyncioTestCase):
    def setUp(self):
        self.customer_id = "__regression_test_customer__"
        db.clear_pending_quote_state(self.customer_id)
        os.environ["FEATURE_BATCH_QUOTE_ENABLED"] = "true"
        os.environ["FEATURE_BATCH_QUOTE_PERCENT"] = "100"
        os.environ["FEATURE_BATCH_QUOTE_SHOPS"] = ""
        os.environ["AI_DYNAMIC_COLLECTION_REPLIES"] = "false"
        os.environ["AI_REWRITE_ALL_REPLIES"] = "false"
        os.environ["BATCH_QUOTE_DELAY_TURNS"] = "0"

    async def test_collect_images_then_ack(self):
        agent = CustomerServiceAgent()
        msg = CustomerMessage(
            msg_id="m1",
            acc_id="test_shop",
            msg="https://img.alicdn.com/a.jpg#*#https://img.alicdn.com/b.jpg",
            from_id=self.customer_id,
            from_name="t",
            cy_id=self.customer_id,
            acc_type="AliWorkbench",
            msg_type=0,
            cy_name="t",
            goods_name="专业找图",
            goods_order="",
        )
        resp = await agent.process_message(msg)
        self.assertTrue(resp.should_reply)
        self.assertTrue(resp.reply.strip())
        st = agent._get_conversation_state(self.customer_id)
        self.assertEqual(len(st.pending_image_urls), 2)

    async def test_finish_signal_triggers_batch_quote(self):
        agent = CustomerServiceAgent()
        st = agent._get_conversation_state(self.customer_id)
        st.pending_image_urls = ["https://img.alicdn.com/a.jpg"]
        st.pending_requirements = ["去背景"]
        agent._sync_pending_quote_state(self.customer_id, st)
        agent._quote_pending_images = AsyncMock(return_value={"reply": "打包15元，确认我就安排", "need_transfer": False})

        msg = CustomerMessage(
            msg_id="m2",
            acc_id="test_shop",
            msg="发完了，报价吧",
            from_id=self.customer_id,
            from_name="t",
            cy_id=self.customer_id,
            acc_type="AliWorkbench",
            msg_type=0,
            cy_name="t",
            goods_name="专业找图",
            goods_order="",
        )
        resp = await agent.process_message(msg)
        self.assertTrue(resp.should_reply)
        self.assertIn("15", resp.reply)
        agent._quote_pending_images.assert_awaited()

    async def test_single_image_requirement_intent_triggers_quote(self):
        agent = CustomerServiceAgent()
        st = agent._get_conversation_state(self.customer_id)
        st.pending_image_urls = ["https://img.alicdn.com/a.jpg"]
        st.pending_requirements = []
        agent._sync_pending_quote_state(self.customer_id, st)
        agent._quote_pending_images = AsyncMock(return_value={"reply": "这张20元，定了我马上做", "need_transfer": False})

        msg = CustomerMessage(
            msg_id="m3",
            acc_id="test_shop",
            msg="这个门头上面的字做一下",
            from_id=self.customer_id,
            from_name="t",
            cy_id=self.customer_id,
            acc_type="AliWorkbench",
            msg_type=0,
            cy_name="t",
            goods_name="专业找图",
            goods_order="",
        )
        resp = await agent.process_message(msg)
        self.assertTrue(resp.should_reply)
        self.assertIn("20", resp.reply)
        agent._quote_pending_images.assert_awaited()

    async def test_multi_image_finish_intent_triggers_quote(self):
        agent = CustomerServiceAgent()
        st = agent._get_conversation_state(self.customer_id)
        st.pending_image_urls = ["https://img.alicdn.com/a.jpg", "https://img.alicdn.com/b.jpg"]
        st.pending_requirements = ["改字"]
        agent._sync_pending_quote_state(self.customer_id, st)
        agent._quote_pending_images = AsyncMock(return_value={"reply": "两张打包45，定了我就开做", "need_transfer": False})

        msg = CustomerMessage(
            msg_id="m4",
            acc_id="test_shop",
            msg="就这几张，先按这些报个价",
            from_id=self.customer_id,
            from_name="t",
            cy_id=self.customer_id,
            acc_type="AliWorkbench",
            msg_type=0,
            cy_name="t",
            goods_name="专业找图",
            goods_order="",
        )
        resp = await agent.process_message(msg)
        self.assertTrue(resp.should_reply)
        self.assertIn("45", resp.reply)
        agent._quote_pending_images.assert_awaited()

    async def test_finish_signal_with_meile_triggers_quote(self):
        agent = CustomerServiceAgent()
        st = agent._get_conversation_state(self.customer_id)
        st.pending_image_urls = ["https://img.alicdn.com/a.jpg"]
        st.pending_requirements = []
        agent._sync_pending_quote_state(self.customer_id, st)
        agent._quote_pending_images = AsyncMock(return_value={"reply": "这张15元，确认就开始", "need_transfer": False})

        msg = CustomerMessage(
            msg_id="m4b",
            acc_id="test_shop",
            msg="没有了，报下价",
            from_id=self.customer_id,
            from_name="t",
            cy_id=self.customer_id,
            acc_type="AliWorkbench",
            msg_type=0,
            cy_name="t",
            goods_name="专业找图",
            goods_order="",
        )
        resp = await agent.process_message(msg)
        self.assertTrue(resp.should_reply)
        self.assertIn("15", resp.reply)
        agent._quote_pending_images.assert_awaited()

    async def test_finish_signal_defers_quote_when_delay_enabled(self):
        os.environ["BATCH_QUOTE_DELAY_TURNS"] = "1"
        agent = CustomerServiceAgent()
        st = agent._get_conversation_state(self.customer_id)
        st.pending_image_urls = ["https://img.alicdn.com/a.jpg"]
        st.pending_requirements = []
        agent._sync_pending_quote_state(self.customer_id, st)
        agent._quote_pending_images = AsyncMock(return_value={"reply": "这张20元，确认就开做", "need_transfer": False})

        msg1 = CustomerMessage(
            msg_id="m4c-1",
            acc_id="test_shop",
            msg="没有了，报价吧",
            from_id=self.customer_id,
            from_name="t",
            cy_id=self.customer_id,
            acc_type="AliWorkbench",
            msg_type=0,
            cy_name="t",
            goods_name="专业找图",
            goods_order="",
        )
        resp1 = await agent.process_message(msg1)
        self.assertTrue(resp1.should_reply)
        self.assertNotIn("20", resp1.reply)
        agent._quote_pending_images.assert_not_awaited()

        msg2 = CustomerMessage(
            msg_id="m4c-2",
            acc_id="test_shop",
            msg="有吗",
            from_id=self.customer_id,
            from_name="t",
            cy_id=self.customer_id,
            acc_type="AliWorkbench",
            msg_type=0,
            cy_name="t",
            goods_name="专业找图",
            goods_order="",
        )
        resp2 = await agent.process_message(msg2)
        self.assertTrue(resp2.should_reply)
        self.assertIn("20", resp2.reply)
        agent._quote_pending_images.assert_awaited()
        os.environ["BATCH_QUOTE_DELAY_TURNS"] = "0"

    async def test_cross_image_composite_intent_triggers_quote(self):
        agent = CustomerServiceAgent()
        st = agent._get_conversation_state(self.customer_id)
        st.pending_image_urls = ["https://img.alicdn.com/a.jpg", "https://img.alicdn.com/b.jpg"]
        st.pending_requirements = []
        agent._sync_pending_quote_state(self.customer_id, st)
        agent._quote_pending_images = AsyncMock(return_value={"reply": "这个跨图合成可以做，打包50元", "need_transfer": False})

        msg = CustomerMessage(
            msg_id="m5",
            acc_id="test_shop",
            msg="A图的图案转换到B图上去",
            from_id=self.customer_id,
            from_name="t",
            cy_id=self.customer_id,
            acc_type="AliWorkbench",
            msg_type=0,
            cy_name="t",
            goods_name="专业找图",
            goods_order="",
        )
        resp = await agent.process_message(msg)
        self.assertTrue(resp.should_reply)
        self.assertIn("50", resp.reply)
        agent._quote_pending_images.assert_awaited()

    async def test_result_followup_query_uses_progress_reply(self):
        agent = CustomerServiceAgent()
        st = agent._get_conversation_state(self.customer_id)
        st.pending_image_urls = ["https://img.alicdn.com/a.jpg"]
        st.pending_requirements = []
        agent._sync_pending_quote_state(self.customer_id, st)
        agent._quote_pending_images = AsyncMock(return_value={"reply": "不应触发报价", "need_transfer": False})

        msg = CustomerMessage(
            msg_id="m5b",
            acc_id="test_shop",
            msg="没找到吗",
            from_id=self.customer_id,
            from_name="t",
            cy_id=self.customer_id,
            acc_type="AliWorkbench",
            msg_type=0,
            cy_name="t",
            goods_name="专业找图",
            goods_order="",
        )
        resp = await agent.process_message(msg)
        self.assertTrue(resp.should_reply)
        self.assertNotIn("不太懂你的意思", resp.reply)
        self.assertNotIn("没完全理解", resp.reply)
        self.assertNotIn("没听明白", resp.reply)
        agent._quote_pending_images.assert_not_awaited()

    async def test_short_youma_followup_uses_progress_reply(self):
        agent = CustomerServiceAgent()
        st = agent._get_conversation_state(self.customer_id)
        st.pending_image_urls = ["https://img.alicdn.com/a.jpg"]
        st.pending_requirements = []
        agent._sync_pending_quote_state(self.customer_id, st)
        agent._quote_pending_images = AsyncMock(return_value={"reply": "不应触发报价", "need_transfer": False})

        msg = CustomerMessage(
            msg_id="m5bb",
            acc_id="test_shop",
            msg="有吗",
            from_id=self.customer_id,
            from_name="t",
            cy_id=self.customer_id,
            acc_type="AliWorkbench",
            msg_type=0,
            cy_name="t",
            goods_name="专业找图",
            goods_order="",
        )
        resp = await agent.process_message(msg)
        self.assertTrue(resp.should_reply)
        self.assertNotIn("不太懂你的意思", resp.reply)
        self.assertNotIn("没完全理解", resp.reply)
        self.assertNotIn("没听明白", resp.reply)
        st2 = agent._get_conversation_state(self.customer_id)
        self.assertEqual(st2.quote_phase, "waiting_result")
        agent._quote_pending_images.assert_not_awaited()

    def test_short_text_classifier(self):
        agent = CustomerServiceAgent()
        self.assertEqual(agent._classify_short_customer_text("有吗"), "progress_query")
        self.assertEqual(agent._classify_short_customer_text("没有了"), "finish_signal")
        self.assertEqual(agent._classify_short_customer_text("好的"), "ack")

    async def test_related_screenshot_followup_is_marked(self):
        agent = CustomerServiceAgent()
        st = agent._get_conversation_state(self.customer_id)
        st.pending_image_urls = ["https://img.alicdn.com/a.jpg"]
        st.pending_requirements = []
        agent._sync_pending_quote_state(self.customer_id, st)

        msg = CustomerMessage(
            msg_id="m5c",
            acc_id="test_shop",
            msg="这是上一张的截图#*#https://img.alicdn.com/b.jpg",
            from_id=self.customer_id,
            from_name="t",
            cy_id=self.customer_id,
            acc_type="AliWorkbench",
            msg_type=0,
            cy_name="t",
            goods_name="专业找图",
            goods_order="",
        )
        resp = await agent.process_message(msg)
        self.assertTrue(resp.should_reply)
        st2 = agent._get_conversation_state(self.customer_id)
        self.assertIn("与上一张相关（截图/局部细节）", st2.pending_requirements)

    async def test_find_image_not_edit_conflict_triggers_clarification(self):
        agent = CustomerServiceAgent()
        st = agent._get_conversation_state(self.customer_id)
        st.pending_image_urls = ["https://img.alicdn.com/a.jpg"]
        st.pending_requirements = []
        agent._sync_pending_quote_state(self.customer_id, st)
        agent._quote_pending_images = AsyncMock(return_value={"reply": "不该触发", "need_transfer": False})

        msg = CustomerMessage(
            msg_id="m6",
            acc_id="test_shop",
            msg="我要你帮我找图，不是让你做图",
            from_id=self.customer_id,
            from_name="t",
            cy_id=self.customer_id,
            acc_type="AliWorkbench",
            msg_type=0,
            cy_name="t",
            goods_name="专业找图",
            goods_order="",
        )
        resp = await agent.process_message(msg)
        self.assertTrue(resp.should_reply)
        self.assertIn("找图", resp.reply)
        self.assertIn("不是做图", resp.reply)
        agent._quote_pending_images.assert_not_awaited()

    async def test_pending_state_restore(self):
        db.update_pending_quote_state(self.customer_id, ["u1", "u2"], ["r1"])
        agent = CustomerServiceAgent()
        st = agent._get_conversation_state(self.customer_id)
        self.assertEqual(st.pending_image_urls, ["u1", "u2"])
        self.assertEqual(st.pending_requirements, ["r1"])

    async def test_collection_reply_never_returns_none(self):
        os.environ["AI_DYNAMIC_COLLECTION_REPLIES"] = "true"
        agent = CustomerServiceAgent()
        agent.agent_natural_reply.run = AsyncMock(side_effect=RuntimeError("mock ai fail"))
        st = agent._get_conversation_state(self.customer_id)
        msg = CustomerMessage(
            msg_id="m-collection-fallback",
            acc_id="test_shop",
            msg="收到没",
            from_id=self.customer_id,
            from_name="t",
            cy_id=self.customer_id,
            acc_type="AliWorkbench",
            msg_type=0,
            cy_name="t",
            goods_name="专业找图",
            goods_order="",
        )
        reply = await agent._render_collection_reply_with_ai(
            message=msg,
            state=st,
            scene="collect_ack",
            intent_hint="确认已收到图片",
            fallback="图片收到了，你继续发就行。",
        )
        self.assertEqual(reply, "图片收到了，你继续发就行。")

    async def test_first_image_ack_avoids_unified_quote_wording(self):
        os.environ["AI_DYNAMIC_COLLECTION_REPLIES"] = "true"
        agent = CustomerServiceAgent()
        st = agent._get_conversation_state(self.customer_id)
        st.pending_image_urls = ["https://img.alicdn.com/a.jpg"]
        msg = CustomerMessage(
            msg_id="m-first-ack",
            acc_id="test_shop",
            msg="https://img.alicdn.com/a.jpg",
            from_id=self.customer_id,
            from_name="t",
            cy_id=self.customer_id,
            acc_type="AliWorkbench",
            msg_type=0,
            cy_name="t",
            goods_name="专业找图",
            goods_order="",
        )
        reply = await agent._render_collection_reply_with_ai(
            message=msg,
            state=st,
            scene="collect_ack",
            intent_hint="确认收到后先承接",
            fallback="图片收到了，你继续发就行。",
        )
        self.assertIn("稍等", reply)
        self.assertNotIn("统一报价", reply)
        self.assertNotIn("发完", reply)

    async def test_map_inquiry_is_rejected(self):
        agent = CustomerServiceAgent()
        msg = CustomerMessage(
            msg_id="m-map-reject",
            acc_id="test_shop",
            msg="这个地图能做吗",
            from_id=self.customer_id,
            from_name="t",
            cy_id=self.customer_id,
            acc_type="AliWorkbench",
            msg_type=0,
            cy_name="t",
            goods_name="专业找图",
            goods_order="",
        )
        resp = await agent.process_message(msg)
        self.assertTrue(resp.should_reply)
        self.assertIn("地图", resp.reply)
        self.assertIn("不做", resp.reply)

    async def test_meaningless_short_text_gets_ping_reply(self):
        agent = CustomerServiceAgent()
        msg = CustomerMessage(
            msg_id="m-meaningless",
            acc_id="test_shop",
            msg="好的",
            from_id=self.customer_id,
            from_name="t",
            cy_id=self.customer_id,
            acc_type="AliWorkbench",
            msg_type=0,
            cy_name="t",
            goods_name="专业找图",
            goods_order="",
        )
        resp = await agent.process_message(msg)
        self.assertTrue(resp.should_reply)
        self.assertIn(resp.reply, ("嗯咯", "嗯啦", "嗯", "哦"))

    async def test_meaningless_short_text_not_ping_when_collecting(self):
        agent = CustomerServiceAgent()
        st = agent._get_conversation_state(self.customer_id)
        st.pending_image_urls = ["https://img.alicdn.com/a.jpg"]
        st.quote_phase = "collecting"
        agent._sync_pending_quote_state(self.customer_id, st)

        msg = CustomerMessage(
            msg_id="m-meaningless-collecting",
            acc_id="test_shop",
            msg="好的",
            from_id=self.customer_id,
            from_name="t",
            cy_id=self.customer_id,
            acc_type="AliWorkbench",
            msg_type=0,
            cy_name="t",
            goods_name="专业找图",
            goods_order="",
        )
        resp = await agent.process_message(msg)
        self.assertTrue(resp.should_reply)
        self.assertTrue((resp.reply or "").strip())
        self.assertNotIn(resp.reply, ("嗯咯", "嗯啦", "嗯", "哦"))

    def tearDown(self):
        db.clear_pending_quote_state(self.customer_id)


if __name__ == "__main__":
    unittest.main(verbosity=2)
