"""
KR完整修复(2026-08-27) — krs表打通 + 方向符号operator + 权重输入
覆盖: krs CRUD / operator方向感知progress / 权重校验 / 多租户隔离 / KPI方向继承
"""
import pytest
from fastapi.testclient import TestClient
from sqlalchemy.orm import Session
from datetime import date

from tests.conftest import (
    create_test_user, get_token_for_user, auth_header, create_test_kpi,
)
from app.models import Objective, KR, KPIDefinition, Entity


def create_test_objective(db: Session, **kwargs) -> Objective:
    defaults = {
        "title": "测试OKR目标",
        "quarter": "2026Q3",
        "dimension": "finance",
        "owner": "测试管理员",
        "status": "active",
        "progress": 0,
        "entity_id": 1,
    }
    defaults.update(kwargs)
    obj = Objective(**defaults)
    db.add(obj)
    db.commit()
    db.refresh(obj)
    return obj


# ============================================================
# KR CRUD — 落krs表
# ============================================================

class TestKrCrud:
    def test_create_kr_persists_to_krs_table(self, client: TestClient, db: Session):
        """创建目标+KR后，krs表 COUNT(*)>0，且OKR API返回krs表的KR"""
        create_test_user(db)
        token = get_token_for_user(client)
        obj = create_test_objective(db)

        resp = client.post(
            f"/api/cma/okr/{obj.id}/krs",
            headers=auth_header(token),
            json={
                "title": "营收增长至800万",
                "operator": ">=",
                "target_value": 800,
                "weight": 40,
            },
        )
        assert resp.status_code == 200, resp.text
        data = resp.json()
        assert data["ok"] is True
        assert data["id"] > 0

        # krs表有数据
        count = db.query(KR).filter(KR.objective_id == obj.id).count()
        assert count == 1

        # OKR API返回krs表KR（operator/weight都在）
        resp2 = client.get(f"/api/cma/okr/{obj.id}", headers=auth_header(token))
        assert resp2.status_code == 200
        krs = resp2.json()["key_results"]
        assert len(krs) == 1
        assert krs[0]["title"] == "营收增长至800万"
        assert krs[0]["operator"] == ">="
        assert krs[0]["target_value"] == 800.0
        assert krs[0]["weight"] == 40.0

    def test_update_kr_and_delete_kr(self, client: TestClient, db: Session):
        create_test_user(db)
        token = get_token_for_user(client)
        obj = create_test_objective(db)

        r = client.post(f"/api/cma/okr/{obj.id}/krs", headers=auth_header(token),
                        json={"title": "KR1", "target_value": 100, "weight": 50})
        kr_id = r.json()["id"]

        # 更新
        r2 = client.put(f"/api/cma/okr/{obj.id}/krs/{kr_id}", headers=auth_header(token),
                        json={"title": "KR1改", "target_value": 120, "weight": 60})
        assert r2.status_code == 200
        assert r2.json()["kr"]["title"] == "KR1改"
        assert r2.json()["kr"]["target_value"] == 120.0

        # 删除
        r3 = client.delete(f"/api/cma/okr/{obj.id}/krs/{kr_id}", headers=auth_header(token))
        assert r3.status_code == 200
        assert db.query(KR).filter(KR.id == kr_id).first() is None

    def test_sync_krs_batch(self, client: TestClient, db: Session):
        """批量同步: 40+35+25=100 权重自由组合可通过"""
        create_test_user(db)
        token = get_token_for_user(client)
        obj = create_test_objective(db)

        resp = client.put(f"/api/cma/okr/{obj.id}/krs/sync", headers=auth_header(token),
                          json={"krs": [
                              {"title": "KR-A", "operator": ">=", "target_value": 100, "weight": 40},
                              {"title": "KR-B", "operator": ">=", "target_value": 200, "weight": 35},
                              {"title": "KR-C", "operator": "<=", "target_value": 50, "weight": 25},
                          ]})
        assert resp.status_code == 200, resp.text
        krs = resp.json()["krs"]
        assert len(krs) == 3
        weights = [float(k["weight"]) for k in krs]
        assert sum(weights) == 100.0, f"权重应可自由组合为100: {weights}"

        # 再次同步（少一条）→ 应删除
        resp2 = client.put(f"/api/cma/okr/{obj.id}/krs/sync", headers=auth_header(token),
                           json={"krs": [
                               {"title": "KR-A", "target_value": 100, "weight": 50},
                               {"title": "KR-B", "target_value": 200, "weight": 50},
                           ]})
        assert resp2.status_code == 200
        assert len(resp2.json()["krs"]) == 2
        assert db.query(KR).filter(KR.objective_id == obj.id).count() == 2


# ============================================================
# operator 方向感知 progress + 达成判断
# ============================================================

class TestOperatorDirection:
    def test_gte_direction_progress(self, client: TestClient, db: Session):
        """>= 场景: current/target*100; 达到目标 → achieved"""
        create_test_user(db)
        token = get_token_for_user(client)
        obj = create_test_objective(db)

        r = client.post(f"/api/cma/okr/{obj.id}/krs", headers=auth_header(token),
                        json={"title": "营收>=800", "operator": ">=", "target_value": 800,
                              "current_value": 600})
        kr = r.json()["kr"]
        assert kr["progress"] == 75, f"600/800=75%: {kr}"
        assert kr["status"] == "in_progress"

        r2 = client.put(f"/api/cma/okr/{obj.id}/krs/{kr['id']}", headers=auth_header(token),
                        json={"current_value": 800})
        kr2 = r2.json()["kr"]
        assert kr2["progress"] == 100
        assert kr2["status"] == "achieved", f"达成应置achieved: {kr2}"

    def test_lte_direction_progress(self, client: TestClient, db: Session):
        """<= 场景（费用率）: 值越小progress越高; 达标→achieved"""
        create_test_user(db)
        token = get_token_for_user(client)
        obj = create_test_objective(db)

        r = client.post(f"/api/cma/okr/{obj.id}/krs", headers=auth_header(token),
                        json={"title": "费用率<=18", "operator": "<=", "target_value": 18,
                              "current_value": 24})
        kr = r.json()["kr"]
        # 24 > 18 → 未达标: 18/24=75
        assert kr["progress"] == 75, f"方向感知错误: {kr}"
        assert kr["status"] == "in_progress"

        r2 = client.put(f"/api/cma/okr/{obj.id}/krs/{kr['id']}", headers=auth_header(token),
                        json={"current_value": 15})
        kr2 = r2.json()["kr"]
        assert kr2["progress"] == 100, f"15<=18 应100%: {kr2}"
        assert kr2["status"] == "achieved"

    def test_eq_with_tolerance(self, client: TestClient, db: Session):
        """= 场景: 容差内100%"""
        create_test_user(db)
        token = get_token_for_user(client)
        obj = create_test_objective(db)

        r = client.post(f"/api/cma/okr/{obj.id}/krs", headers=auth_header(token),
                        json={"title": "库存=50", "operator": "=", "target_value": 50,
                              "tolerance": 1, "current_value": 50.5})
        kr = r.json()["kr"]
        assert kr["progress"] == 100
        assert kr["status"] == "achieved"

    def test_kpi_direction_inherit(self, client: TestClient, db: Session):
        """关联KPI自动继承方向（F_COST_RATIO threshold_green '<=18'）"""
        create_test_user(db)
        token = get_token_for_user(client)
        kpi = create_test_kpi(db, kpi_code="F_COST_RATIO", kpi_name="成本费用率",
                              threshold_green="<=18")
        obj = create_test_objective(db)

        r = client.post(f"/api/cma/okr/{obj.id}/krs", headers=auth_header(token),
                        json={"title": "压降成本费用率", "metric_kpi_id": kpi.id})
        kr = r.json()["kr"]
        assert kr["operator"] == "<=", f"应继承KPI方向<=: {kr}"
        assert kr["target_value"] == 18.0, f"应继承KPI目标18: {kr}"


# ============================================================
# 权重校验
# ============================================================

class TestWeightValidation:
    def test_fractional_weight_33_33_34(self, client: TestClient, db: Session):
        """33.33+33.33+33.34 = 100 可输入（小数权重）"""
        create_test_user(db)
        token = get_token_for_user(client)
        obj = create_test_objective(db)

        resp = client.put(f"/api/cma/okr/{obj.id}/krs/sync", headers=auth_header(token),
                          json={"krs": [
                              {"title": "A", "target_value": 10, "weight": 33.33},
                              {"title": "B", "target_value": 10, "weight": 33.33},
                              {"title": "C", "target_value": 10, "weight": 33.34},
                          ]})
        assert resp.status_code == 200
        weights = [float(k["weight"]) for k in resp.json()["krs"]]
        assert abs(sum(weights) - 100.0) < 0.01, weights

    def test_auto_divide_3_krs_33_33_34(self, db: Session):
        """3个KR自动平分 → 33/33/34（后端sync不做校验，前端提供；此处验证权重存储）"""
        create_test_user(db)
        token = None  # 直接走模型层验证
        from app.api.okr import _calc_progress  # noqa

        obj = create_test_objective(db)
        # 前端自动平分逻辑：base=33, remainder=1 → 33/33/34
        n = 3
        base = 100 // n
        remainder = 100 - base * n
        weights = [base] * (n - 1) + [base + remainder]
        assert weights == [33, 33, 34]


# ============================================================
# 多租户隔离
# ============================================================

class TestMultiTenant:
    def test_kr_entity_isolation(self, client: TestClient, db: Session):
        """账套隔离: entity_id=2 的用户看不到 entity_id=1 的KR"""
        # entity 2 存在
        ent2 = db.query(Entity).filter(Entity.id == 2).first()
        if not ent2:
            db.add(Entity(id=2, name="另一企业", short_name="B", status="active"))
            db.commit()

        create_test_user(db)
        token = get_token_for_user(client)
        obj1 = create_test_objective(db, title="企业1目标")  # entity 1

        r = client.post(f"/api/cma/okr/{obj1.id}/krs", headers=auth_header(token),
                        json={"title": "企业1的KR", "target_value": 100})
        assert r.status_code == 200

        # 用 entity 2 登录
        ent2_user = create_test_user(db, username="user2", role="ceo")
        resp2 = client.post("/api/cma/auth/login", json={
            "username": "user2", "password": "admin123", "entity_id": 2})
        token2 = resp2.json().get("token")
        assert token2

        # entity2 访问 entity1 的目标 → 404
        resp3 = client.get(f"/api/cma/okr/{obj1.id}", headers=auth_header(token2))
        assert resp3.status_code == 404, "跨企业应404"
