from __future__ import annotations

import json

import httpx

from backend.bootstrap.application import create_application
from backend.bootstrap.settings import Settings
from tests.support import run_scenario
from tests.test_accounts import (
    ADMIN_PASSWORD,
    USER_PASSWORD,
    csrf_headers,
    register,
    use_session,
)


def _formula() -> dict:
    return {
        "universe": {"exclude_st": True, "listed_days_min": 120},
        "filters": [{"field": "close", "op": ">", "value": 1}],
        "score": [{"field": "close", "weight": 1, "direction": "desc"}],
        "limit": 10,
        "min_score": 0.5,
    }


class FormulaProvider:
    def stream(self, _profile, _messages):
        yield json.dumps(
            {
                "universe": {"exclude_st": True, "listed_days_min": 120},
                "filters": [{"field": "amount_billion", "op": ">=", "value": 3}],
                "score": [
                    {"field": "return_20d", "weight": 60, "direction": "desc"},
                    {"field": "sector_strength", "weight": 40, "direction": "desc"},
                ],
                "limit": 20,
                "min_score": 55,
            },
            ensure_ascii=False,
        )


def test_screener_catalog_lock_and_custom_strategy_boundary(tmp_path) -> None:
    application = create_application(Settings.for_test(tmp_path))

    async def scenario(client: httpx.AsyncClient) -> None:
        _, admin = await register(client, "admin-screener", ADMIN_PASSWORD)
        client.cookies.clear()
        _, regular = await register(client, "regular-screener", USER_PASSWORD)

        catalog = await client.get("/api/screener/catalog")
        assert catalog.status_code == 200
        assert len(catalog.json()["factors"]) == 109

        locked = await client.get("/api/screener?date=2026-07-30")
        assert locked.status_code == 403
        assert locked.json()["error"]["code"] == "membership_required"
        rejected = await client.put(
            "/api/screener/custom",
            headers=csrf_headers(regular),
            json={"name": "越权策略", "formula": _formula()},
        )
        assert rejected.status_code == 403
        denied_compile = await client.post(
            "/api/screener/formula/compile",
            headers=csrf_headers(regular),
            json={"description": "选择成交活跃且趋势较强的股票"},
        )
        assert denied_compile.status_code == 403

        use_session(client, admin)
        model = await client.post(
            "/api/admin/models",
            headers=csrf_headers(admin),
            json={
                "display_name": "公式模型",
                "base_url": "https://model.example.com/v1",
                "model_identifier": "formula-model",
                "api_key": "formula-secret",
            },
        )
        assert model.status_code == 201
        application.state.container.llm._provider = FormulaProvider()
        compiled = await client.post(
            "/api/screener/formula/compile",
            headers=csrf_headers(admin),
            json={"description": "成交额至少3亿元，偏重20日走势和板块强度"},
        )
        assert compiled.status_code == 200
        assert [item["weight"] for item in compiled.json()["formula"]["score"]] == [
            0.6,
            0.4,
        ]
        assert compiled.json()["formula"]["min_score"] == 0.55
        workspace = await client.get("/api/screener?date=2026-07-30")
        assert workspace.status_code == 200
        assert workspace.json()["trade_date"] is None
        saved = await client.put(
            "/api/screener/custom",
            headers=csrf_headers(admin),
            json={"name": "我的策略", "formula": _formula()},
        )
        assert saved.status_code == 200
        assert saved.json()["name"] == "我的策略"
        refreshed = await client.get("/api/screener?date=2026-07-30")
        assert [item["name"] for item in refreshed.json()["custom_strategies"]] == ["我的策略"]

    run_scenario(application, scenario)
