from __future__ import annotations

import json

import pytest

from core.models import Post
from decode_content.payloads import IngestPayloadValidationError, build_payload, build_payloads, validate_ingest_payload


def _post() -> Post:
    return Post(
        id="xhs_1",
        platform="xiaohongshu",
        url="https://xhs/1",
        content_id="1",
        unique_key="xhs:1",
        title="脚本教程",
        content_type="note",
        content_mode="image_post",
        media_count=2,
        cover_url="https://cdn.test/cover.webp",
        author_name="作者",
    )


def test_build_how_payload_uses_formal_dim_attribute_and_scope_union():
    knowledge = {
        "id": "k1",
        "type": "how",
        "title": "先定受众再写脚本",
        "purpose": "写出稳定脚本",
        "业务阶段": ["脚本"],
        "steps": [
            {
                "input": "选题",
                "directive": "收窄受众",
                "output": "受众画像",
                "创作阶段": "定向",
                "动作": "收窄",
                "作用域": [
                    {"scope_type": "intent", "value": "吸引注意", "link": "复用"},
                    {"scope_type": "intent", "value": "吸引注意", "link": "复用"},
                ],
            }
        ],
    }

    payload = build_payload(_post(), knowledge)

    assert payload["dim_attributes"] == ["how"]
    assert payload["source"]["source_metadata"] == {
        "platform": "xiaohongshu",
        "url": "https://xhs/1",
        "unique_key": "xhs:1",
        "platform_item_id": "1",
        "content_type": "note",
        "content_mode": "image_post",
        "media_count": 2,
        "cover_url": "https://cdn.test/cover.webp",
    }
    assert "输入：选题" in payload["content"]
    assert "指引：收窄受众" in payload["content"]
    assert payload["scopes"] == [{"scope_type": "intent", "value": "吸引注意"}]
    assert {"key": "创作阶段", "type": "str", "value": "定向"} in payload["custom_ext"]


def test_build_what_and_why_payload_content_is_string_contract():
    what = {
        "id": "w1",
        "type": "what",
        "title": "脚本三要素",
        "kind": "子集",
        "概要": "脚本由开头中段结尾构成",
        "维度拆分规则": "按表达顺序",
        "body": [{"item_name": "开头", "item_desc": "抓注意"}],
        "业务阶段": ["脚本"],
        "作用域": [{"scope_type": "form", "value": "脚本结构", "top": []}],
    }
    why = {
        "id": "y1",
        "type": "why",
        "title": "冲突带来停留",
        "阐述": "冲突能制造期待，所以提升停留。",
        "业务阶段": ["选题"],
        "作用域": [{"scope_type": "effect", "value": "提升停留", "score": 0.95}],
    }

    what_payload, why_payload = build_payloads(_post(), [what, why])

    assert what_payload["dim_attributes"] == ["what"]
    assert json.loads(what_payload["content"])["body"][0]["item_name"] == "开头"
    assert {"key": "概要", "type": "str", "value": "脚本由开头中段结尾构成"} in what_payload["custom_ext"]
    assert why_payload["dim_attributes"] == ["why"]
    assert why_payload["content"] == "冲突能制造期待，所以提升停留。"
    assert "score" not in why_payload["scopes"][0]


def test_build_why_payload_accepts_explanation_alias():
    payload = build_payload(
        _post(),
        {
            "id": "y2",
            "type": "why",
            "title": "动作变化带来画面节奏",
            "explanation": "动作变化能制造连续观看的期待。",
            "业务阶段": ["脚本"],
            "作用域": [{"scope_type": "effect", "value": "提升节奏感"}],
        },
    )

    assert payload["content"] == "动作变化能制造连续观看的期待。"


def test_validate_ingest_payload_blocks_external_api_contract_violations():
    payload = build_payload(
        _post(),
        {
            "id": "h1",
            "type": "how",
            "title": "先定受众再写脚本",
            "purpose": "写出稳定脚本",
            "业务阶段": ["脚本"],
            "steps": [
                {
                    "input": "选题",
                    "directive": "收窄受众",
                    "output": "受众画像",
                    "作用域": [{"scope_type": "intent", "value": "吸引注意"}],
                }
            ],
        },
    )

    bad = {**payload, "content": ""}
    with pytest.raises(IngestPayloadValidationError, match="content"):
        validate_ingest_payload(bad)

    bad = {**payload, "source": {**payload["source"], "id": "x" * 65}}
    with pytest.raises(IngestPayloadValidationError, match="source.id"):
        validate_ingest_payload(bad)

    bad = {**payload, "title": "x" * 513}
    with pytest.raises(IngestPayloadValidationError, match="title"):
        validate_ingest_payload(bad)

    bad = {**payload, "source": {**payload["source"], "author": "x" * 129}}
    with pytest.raises(IngestPayloadValidationError, match="author"):
        validate_ingest_payload(bad)

    bad = {**payload, "scopes": [{"scope_type": "intent", "value": "x" * 129}]}
    with pytest.raises(IngestPayloadValidationError, match="scopes"):
        validate_ingest_payload(bad)

    bad = {**payload, "custom_ext": [{"key": "推荐指数", "type": "bad", "value": "5"}]}
    with pytest.raises(IngestPayloadValidationError, match="custom_ext"):
        validate_ingest_payload(bad)


def test_validate_ingest_payload_rejects_legacy_dim_attributes():
    payload = build_payload(
        _post(),
        {
            "id": "h1",
            "type": "how",
            "title": "先定受众再写脚本",
            "purpose": "写出稳定脚本",
            "业务阶段": ["脚本"],
            "steps": [
                {
                    "input": "选题",
                    "directive": "收窄受众",
                    "output": "受众画像",
                    "作用域": [{"scope_type": "intent", "value": "吸引注意"}],
                }
            ],
        },
    )
    legacy_values = [
        "how" + "\u5de5\u5e8f",
        "what" + "\u6784\u6210",
        "why" + "\u539f\u7406",
    ]

    for legacy_value in legacy_values:
        bad = {**payload, "dim_attributes": [legacy_value]}
        with pytest.raises(IngestPayloadValidationError, match="dim_attributes"):
            validate_ingest_payload(bad)
