"""预算管理模块测试 — 预算计划CRUD + 自动分解 + 版本"""
import pytest
from fastapi.testclient import TestClient
from sqlalchemy.orm import Session
from tests.conftest import create_test_user, get_token_for_user, auth_header, create_test_kpi


class TestBudgetPlans:
    """预算计划CRUD测试"""

    BASE = "/api/cma/budget"

    def test_list_plans_empty(self, client: TestClient, db: Session):
        """空列表"""
        create_test_user(db)
        token = get_token_for_user(client)

        resp = client.get(f"{self.BASE}/plans", headers=auth_header(token))
        assert resp.status_code == 200
        assert resp.json()["data"] == []

    def test_create_plan(self, client: TestClient, db: Session):
        """创建预算计划"""
        create_test_user(db)
        token = get_token_for_user(client)
        kpi = create_test_kpi(db, kpi_code="BUDGET_KPI_01")

        resp = client.post(
            f"{self.BASE}/plans",
            headers=auth_header(token),
            json={"kpi_id": kpi.id, "period": "2026-06", "budget_value": 50000.0},
        )
        assert resp.status_code == 200
        assert resp.json()["message"] == "预算已创建"
        assert resp.json()["id"] > 0

    def test_create_plan_duplicate_upsert(self, client: TestClient, db: Session):
        """重复创建同一KPI+期间 → 更新而非新增"""
        create_test_user(db)
        token = get_token_for_user(client)
        kpi = create_test_kpi(db, kpi_code="BUDGET_KPI_02")

        # 创建
        client.post(
            f"{self.BASE}/plans",
            headers=auth_header(token),
            json={"kpi_id": kpi.id, "period": "2026-06", "budget_value": 30000.0},
        )
        # 再次创建（更新）
        resp = client.post(
            f"{self.BASE}/plans",
            headers=auth_header(token),
            json={"kpi_id": kpi.id, "period": "2026-06", "budget_value": 35000.0},
        )
        assert resp.status_code == 200
        assert resp.json()["message"] == "预算已更新"

    def test_create_plan_missing_fields(self, client: TestClient, db: Session):
        """缺少必要参数被拒绝"""
        create_test_user(db)
        token = get_token_for_user(client)

        resp = client.post(
            f"{self.BASE}/plans",
            headers=auth_header(token),
            json={"kpi_id": 1},  # 缺少 period 和 budget_value
        )
        assert resp.status_code == 400

    def test_update_plan(self, client: TestClient, db: Session):
        """更新预算计划"""
        create_test_user(db)
        token = get_token_for_user(client)
        kpi = create_test_kpi(db, kpi_code="BUDGET_KPI_03")

        create_resp = client.post(
            f"{self.BASE}/plans",
            headers=auth_header(token),
            json={"kpi_id": kpi.id, "period": "2026-06", "budget_value": 100.0},
        )
        plan_id = create_resp.json()["id"]

        resp = client.put(
            f"{self.BASE}/plans/{plan_id}",
            headers=auth_header(token),
            json={"budget_value": 200.0, "remark": "已更新"},
        )
        assert resp.status_code == 200

    def test_delete_plan(self, client: TestClient, db: Session):
        """删除预算计划"""
        create_test_user(db)
        token = get_token_for_user(client)
        kpi = create_test_kpi(db, kpi_code="BUDGET_KPI_04")

        create_resp = client.post(
            f"{self.BASE}/plans",
            headers=auth_header(token),
            json={"kpi_id": kpi.id, "period": "2026-06", "budget_value": 100.0},
        )
        plan_id = create_resp.json()["id"]

        resp = client.delete(f"{self.BASE}/plans/{plan_id}", headers=auth_header(token))
        assert resp.status_code == 200
        assert resp.json()["message"] == "预算已删除"

        # 验证已删除
        get_resp = client.get(f"{self.BASE}/plans", headers=auth_header(token))
        ids = [p["id"] for p in get_resp.json()["data"]]
        assert plan_id not in ids


class TestBudgetAutoDecompose:
    """预算自动分解测试"""

    BASE = "/api/cma/budget"

    def test_auto_decompose_single_kpi(self, client: TestClient, db: Session):
        """单KPI自动分解年度预算为月度（均分）"""
        create_test_user(db)
        token = get_token_for_user(client)
        kpi = create_test_kpi(db, kpi_code="BUDGET_DECOMPOSE")

        resp = client.post(
            f"{self.BASE}/auto-decompose",
            headers=auth_header(token),
            json={"kpi_id": kpi.id, "annual_budget": 120000, "year": 2026, "method": "equal"},
        )
        assert resp.status_code == 200
        data = resp.json()
        assert "已分解" in data["message"]
        assert len(data["monthly_budgets"]) == 12
        # 年度预算120000，12个月均分，每月10000
        assert data["monthly_budgets"][0]["value"] == 10000.0

    def test_auto_decompose_missing(self, client: TestClient, db: Session):
        """没有年度预算数据时尝试分解 → 400"""
        create_test_user(db)
        token = get_token_for_user(client)
        create_test_kpi(db, kpi_code="BUDGET_NO_DATA")

        resp = client.post(
            f"{self.BASE}/auto-decompose",
            headers=auth_header(token),
            json={"year": 2026, "method": "equal"},
        )
        assert resp.status_code == 400


class TestBudgetVersions:
    """预算版本管理测试"""

    BASE = "/api/cma/budget"

    def test_create_and_submit_version(self, client: TestClient, db: Session):
        """创建预算后查询版本并提交（端点已移除，预期404）"""
        create_test_user(db)
        token = get_token_for_user(client)
        kpi = create_test_kpi(db, kpi_code="BUDGET_VER_KPI")

        # 创建一条预算
        client.post(
            f"{self.BASE}/plans",
            headers=auth_header(token),
            json={"kpi_id": kpi.id, "period": "2026-06", "budget_value": 10000.0},
        )

        # 版本端点已移除
        ver_resp = client.get(f"{self.BASE}/versions", headers=auth_header(token))
        assert ver_resp.status_code == 404, "versions端点已移除"

    def test_approve_version(self, client: TestClient, db: Session):
        """审批通过版本（端点已移除，预期404）"""
        create_test_user(db)
        token = get_token_for_user(client)

        resp = client.post(
            f"{self.BASE}/versions/approve",
            headers=auth_header(token),
            json={"version": "v1.0", "action": "approved"},
        )
        assert resp.status_code == 404, "versions/approve端点已移除"

    def test_reject_version(self, client: TestClient, db: Session):
        """驳回版本（端点已移除，预期404）"""
        create_test_user(db)
        token = get_token_for_user(client)

        resp = client.post(
            f"{self.BASE}/versions/approve",
            headers=auth_header(token),
            json={"version": "v2.0", "action": "rejected"},
        )
        assert resp.status_code == 404, "versions/approve端点已移除"
