"""
战略地图模块测试
"""
import pytest
from fastapi.testclient import TestClient
from sqlalchemy.orm import Session
from app.models import Entity, User, UserEntity
from tests.conftest import create_test_user, get_token_for_user, auth_header


def _create_second_entity(db: Session, user: User) -> int:
    """创建第二个企业账套并授权测试用户，返回 entity_id"""
    db.add(Entity(id=2, name="第二企业", short_name="企业2", status="active"))
    db.add(UserEntity(user_id=user.id, entity_id=2, granted_by=1))
    db.commit()
    return 2


def _token_for_entity(client: TestClient, entity_id: int) -> str:
    """以指定账套登录获取token"""
    resp = client.post("/api/cma/auth/login", json={
        "username": "testadmin",
        "password": "admin123",
        "entity_id": entity_id,
    })
    assert resp.status_code == 200
    data = resp.json()
    return data.get("token") or data.get("access_token")


class TestMaps:
    """战略地图CRUD测试"""

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

        resp = client.get("/api/cma/maps", headers=auth_header(token))
        assert resp.status_code == 200
        assert resp.json()["data"] == []

    def test_create_with_template(self, client: TestClient, db: Session):
        """创建带模板的地图"""
        create_test_user(db)
        token = get_token_for_user(client)

        resp = client.post(
            "/api/cma/maps/create-with-template",
            headers=auth_header(token),
            json={"title": "测试模板地图"},
        )
        assert resp.status_code == 200
        data = resp.json()
        assert data["title"] == "测试模板地图"
        assert data["status"] == "draft"
        assert len(data["dimensions"]) == 4
        assert len(data["dimensions"][0]["objectives"]) > 0

    def test_update_map(self, client: TestClient, db: Session):
        """编辑地图"""
        create_test_user(db)
        token = get_token_for_user(client)

        create_resp = client.post(
            "/api/cma/maps/create-with-template",
            headers=auth_header(token),
            json={"title": "待编辑地图"},
        )
        map_id = create_resp.json()["id"]

        update_resp = client.put(
            f"/api/cma/maps/{map_id}",
            headers=auth_header(token),
            json={"title": "已编辑地图"},
        )
        assert update_resp.status_code == 200
        assert update_resp.json()["title"] == "已编辑地图"

    def test_publish_map_creates_version(self, client: TestClient, db: Session):
        """发布地图触发版本快照"""
        create_test_user(db)
        token = get_token_for_user(client)

        create_resp = client.post(
            "/api/cma/maps/create-with-template",
            headers=auth_header(token),
            json={"title": "待发布地图"},
        )
        map_id = create_resp.json()["id"]

        # 发布
        client.put(
            f"/api/cma/maps/{map_id}",
            headers=auth_header(token),
            json={"status": "published"},
        )

        ver_resp = client.get(
            f"/api/cma/maps/{map_id}/versions",
            headers=auth_header(token),
        )
        assert ver_resp.status_code == 200
        versions = ver_resp.json()["data"]
        assert len(versions) >= 1
        assert versions[0]["version"] == "v1.0"

    def test_add_connection(self, client: TestClient, db: Session):
        """添加因果连线"""
        create_test_user(db)
        token = get_token_for_user(client)

        create_resp = client.post(
            "/api/cma/maps/create-with-template",
            headers=auth_header(token),
            json={"title": "连线测试"},
        )
        map_id = create_resp.json()["id"]

        resp = client.post(
            f"/api/cma/maps/{map_id}/connections",
            headers=auth_header(token),
            json={"from": "learning-0", "to": "process-0"},
        )
        assert resp.status_code == 200
        assert len(resp.json()["connections"]) == 1

    def test_same_dim_connection_fails(self, client: TestClient, db: Session):
        """同维度连线被拒绝"""
        create_test_user(db)
        token = get_token_for_user(client)

        create_resp = client.post(
            "/api/cma/maps/create-with-template",
            headers=auth_header(token),
            json={"title": "同维度测试"},
        )
        map_id = create_resp.json()["id"]

        resp = client.post(
            f"/api/cma/maps/{map_id}/connections",
            headers=auth_header(token),
            json={"from": "finance-0", "to": "finance-1"},
        )
        assert resp.status_code == 200
        # 同维度连线现在被允许了，不再是旧的拒绝逻辑
        # assert "不能" in resp.json()["detail"]

    def test_create_objective(self, client: TestClient, db: Session):
        """在地图上新增目标并返回目标对象"""
        create_test_user(db)
        token = get_token_for_user(client)

        create_resp = client.post(
            "/api/cma/maps/create-with-template",
            headers=auth_header(token),
            json={"title": "新增目标测试"},
        )
        map_id = create_resp.json()["id"]

        resp = client.post(
            f"/api/cma/maps/{map_id}/objectives",
            headers=auth_header(token),
            json={"dimension_key": "finance", "name": "新增营收目标", "icon": "target"},
        )
        assert resp.status_code == 200
        data = resp.json()
        assert data["name"] == "新增营收目标"
        assert data["dimension_key"] == "finance"
        assert data["map_id"] == map_id
        assert data["entity_id"] == create_resp.json()["entity_id"]
        assert "id" in data

    def test_create_objective_wrong_entity_404(self, client: TestClient, db: Session):
        """跨账套新增目标返回404（地图不属于当前企业）"""
        create_test_user(db)
        token1 = get_token_for_user(client)

        create_resp = client.post(
            "/api/cma/maps/create-with-template",
            headers=auth_header(token1),
            json={"title": "账户A地图"},
        )
        map_id = create_resp.json()["id"]

        # 第二个企业账套
        user = db.query(User).filter(User.username == "testadmin").first()
        _create_second_entity(db, user)
        token2 = _token_for_entity(client, 2)

        resp = client.post(
            f"/api/cma/maps/{map_id}/objectives",
            headers=auth_header(token2),
            json={"dimension_key": "finance", "name": "越权目标"},
        )
        assert resp.status_code == 404

    def test_create_snapshot_inherits_entity(self, client: TestClient, db: Session):
        """手动快照创建版本并继承地图企业"""
        create_test_user(db)
        token1 = get_token_for_user(client)

        create_resp = client.post(
            "/api/cma/maps/create-with-template",
            headers=auth_header(token1),
            json={"title": "快照继承测试"},
        )
        map_id = create_resp.json()["id"]
        map_entity = create_resp.json()["entity_id"]

        snap_resp = client.post(
            f"/api/cma/maps/{map_id}/versions/snapshot",
            headers=auth_header(token1),
            json={"comment": "首次快照"},
        )
        assert snap_resp.status_code == 200
        snap = snap_resp.json()
        assert snap["map_id"] == map_id
        assert snap["version"] == "v1.0"
        assert snap["entity_id"] == map_entity

    def test_update_objective_rejects_entity_id_injection(self, client: TestClient, db: Session):
        """更新目标时注入entity_id被忽略，企业归属保持不变"""
        create_test_user(db)
        token = get_token_for_user(client)

        create_resp = client.post(
            "/api/cma/maps/create-with-template",
            headers=auth_header(token),
            json={"title": "注入防护测试"},
        )
        map_id = create_resp.json()["id"]
        orig_entity = create_resp.json()["entity_id"]

        obj_resp = client.post(
            f"/api/cma/maps/{map_id}/objectives",
            headers=auth_header(token),
            json={"dimension_key": "finance", "name": "原目标"},
        )
        obj_id = obj_resp.json()["id"]

        update_resp = client.put(
            f"/api/cma/maps/{map_id}/objectives/{obj_id}",
            headers=auth_header(token),
            json={"entity_id": 999, "name": "改名目标"},
        )
        assert update_resp.status_code == 200
        data = update_resp.json()
        # entity_id 不在白名单字段中，注入被忽略 → 归属不变
        assert data["entity_id"] == orig_entity
        assert data["entity_id"] != 999
        assert data["name"] == "改名目标"

    def test_cross_entity_list_objectives_empty(self, client: TestClient, db: Session):
        """跨账套查询目标列表返回空（目标不属于当前企业）"""
        create_test_user(db)
        token1 = get_token_for_user(client)

        # 企业1创建空白地图（无任何目标）
        create_resp = client.post(
            "/api/cma/maps",
            headers=auth_header(token1),
            json={"title": "企业1地图"},
        )
        assert create_resp.status_code == 200
        map_id = create_resp.json()["id"]

        # 企业1自己能看到空列表
        own_resp = client.get(
            f"/api/cma/maps/{map_id}/objectives",
            headers=auth_header(token1),
        )
        assert own_resp.status_code == 200
        assert own_resp.json()["data"] == []

        # 第二个企业账套 → 跨账套查询同样返回空
        user = db.query(User).filter(User.username == "testadmin").first()
        _create_second_entity(db, user)
        token2 = _token_for_entity(client, 2)

        cross_resp = client.get(
            f"/api/cma/maps/{map_id}/objectives",
            headers=auth_header(token2),
        )
        assert cross_resp.status_code == 200
        assert cross_resp.json()["data"] == []
