from __future__ import annotations

from collections.abc import Generator

from auth_helpers import install_legacy_header_auth_override
from fastapi.testclient import TestClient
from sqlalchemy import create_engine, select
from sqlalchemy.orm import Session, sessionmaker
from sqlalchemy.pool import StaticPool

from app.api.deps import get_db
from app.db.base import Base
from app.main import create_app
from app.models.ai_memory import MemoryEntry
from app.models.organization import OrganizationUnit


def _build_client() -> tuple[TestClient, sessionmaker[Session]]:
    engine = create_engine(
        "sqlite+pysqlite:///:memory:",
        connect_args={"check_same_thread": False},
        poolclass=StaticPool,
    )
    Base.metadata.create_all(bind=engine)
    session_factory = sessionmaker(bind=engine, autoflush=False, autocommit=False)
    with session_factory() as db:
        db.add(
            OrganizationUnit(
                id="department-finance",
                unit_code="FINANCE",
                name="财务部",
                unit_type="department",
            )
        )
        db.commit()

    app = create_app()
    install_legacy_header_auth_override(app)

    def override_db() -> Generator[Session, None, None]:
        with session_factory() as db:
            yield db

    app.dependency_overrides[get_db] = override_db
    return TestClient(app), session_factory


def _headers(*, tenant_id: str = "tenant-a", admin: bool = True) -> dict[str, str]:
    return {
        "X-Auth-Username": "admin@example.com" if admin else "employee@example.com",
        "X-Auth-Name": "Tenant Admin" if admin else "Employee",
        "X-Auth-Tenant-Id": tenant_id,
        "X-Auth-Is-Admin": "true" if admin else "false",
        "X-Auth-Role-Codes": "manager" if admin else "user",
    }


def test_organization_memory_api_requires_platform_admin() -> None:
    client, _ = _build_client()

    response = client.post(
        "/api/v1/expense-application-memories/organization",
        headers=_headers(admin=False),
        json={
            "scope_type": "enterprise",
            "value": "火车",
            "expires_in_days": 180,
            "reason": "统一差旅基线",
            "request_id": "create-enterprise-memory-denied",
        },
    )

    assert response.status_code == 403


def test_organization_memory_api_lifecycle_and_tenant_isolation() -> None:
    client, session_factory = _build_client()
    created = client.post(
        "/api/v1/expense-application-memories/organization",
        headers=_headers(),
        json={
            "scope_type": "enterprise",
            "value": "火车",
            "expires_in_days": 180,
            "reason": "统一差旅基线",
            "request_id": "create-enterprise-memory-a",
        },
    )
    assert created.status_code == 201, created.text
    first = created.json()
    assert first["scope_id"] == "tenant-a"
    assert first["generation"] == 1
    assert first["can_revoke"] is True

    replayed = client.post(
        "/api/v1/expense-application-memories/organization",
        headers=_headers(),
        json={
            "scope_type": "enterprise",
            "value": "火车",
            "expires_in_days": 180,
            "reason": "统一差旅基线",
            "request_id": "create-enterprise-memory-a",
        },
    )
    assert replayed.status_code == 201
    assert replayed.json()["id"] == first["id"]
    changed_create_payload = client.post(
        "/api/v1/expense-application-memories/organization",
        headers=_headers(),
        json={
            "scope_type": "enterprise",
            "value": "飞机",
            "expires_in_days": 180,
            "reason": "同键不同请求体",
            "request_id": "create-enterprise-memory-a",
        },
    )
    assert changed_create_payload.status_code == 409

    duplicate = client.post(
        "/api/v1/expense-application-memories/organization",
        headers=_headers(),
        json={
            "scope_type": "enterprise",
            "value": "轮船",
            "expires_in_days": 180,
            "reason": "不应静默覆盖",
            "request_id": "create-enterprise-memory-b",
        },
    )
    assert duplicate.status_code == 409

    updated = client.put(
        f"/api/v1/expense-application-memories/organization/{first['id']}",
        headers=_headers(),
        json={
            "value": "飞机",
            "expires_in_days": 120,
            "expected_generation": 1,
            "reason": "制度版本调整",
            "request_id": "update-enterprise-memory-a",
        },
    )
    assert updated.status_code == 200, updated.text
    second = updated.json()
    assert second["generation"] == 2
    assert second["value"] == "飞机"
    changed_update_payload = client.put(
        f"/api/v1/expense-application-memories/organization/{first['id']}",
        headers=_headers(),
        json={
            "value": "轮船",
            "expires_in_days": 120,
            "expected_generation": 1,
            "reason": "同键不同请求体",
            "request_id": "update-enterprise-memory-a",
        },
    )
    assert changed_update_payload.status_code == 409

    stale_old_id = client.put(
        f"/api/v1/expense-application-memories/organization/{first['id']}",
        headers=_headers(),
        json={
            "value": "轮船",
            "expected_generation": 1,
            "reason": "旧地址更新",
            "request_id": "update-enterprise-memory-old-id",
        },
    )
    assert stale_old_id.status_code == 409

    stale = client.put(
        f"/api/v1/expense-application-memories/organization/{second['id']}",
        headers=_headers(),
        json={
            "value": "轮船",
            "expected_generation": 1,
            "reason": "陈旧页面更新",
            "request_id": "update-enterprise-memory-stale",
        },
    )
    assert stale.status_code == 409

    assert client.get(
        "/api/v1/expense-application-memories/organization",
        headers=_headers(tenant_id="tenant-b"),
    ).json() == {"items": []}
    hidden = client.post(
        f"/api/v1/expense-application-memories/organization/{second['id']}/revoke",
        headers=_headers(tenant_id="tenant-b"),
        json={
            "expected_generation": 2,
            "reason": "越权尝试",
            "request_id": "revoke-other-tenant",
        },
    )
    assert hidden.status_code == 404

    revoked = client.post(
        f"/api/v1/expense-application-memories/organization/{second['id']}/revoke",
        headers=_headers(),
        json={
            "expected_generation": 2,
            "reason": "改用新制度",
            "request_id": "revoke-enterprise-memory-a",
        },
    )
    assert revoked.status_code == 200, revoked.text
    replayed_revoke = client.post(
        f"/api/v1/expense-application-memories/organization/{second['id']}/revoke",
        headers=_headers(),
        json={
            "expected_generation": 2,
            "reason": "改用新制度",
            "request_id": "revoke-enterprise-memory-a",
        },
    )
    assert replayed_revoke.status_code == 200
    assert replayed_revoke.json() == revoked.json()
    changed_revoke_payload = client.post(
        f"/api/v1/expense-application-memories/organization/{second['id']}/revoke",
        headers=_headers(),
        json={
            "expected_generation": 2,
            "reason": "同键不同请求体",
            "request_id": "revoke-enterprise-memory-a",
        },
    )
    assert changed_revoke_payload.status_code == 409
    with session_factory() as db:
        entries = list(
            db.scalars(
                select(MemoryEntry)
                .where(MemoryEntry.tenant_id == "tenant-a")
                .order_by(MemoryEntry.generation.asc())
            ).all()
        )
        assert [entry.status for entry in entries] == ["suppressed", "revoked"]
        assert entries[0].superseded_by_id == entries[1].id
        assert entries[0].management_request_id == "create-enterprise-memory-a"
        assert entries[0].management_payload_fingerprint.startswith("hmac-sha256:")
        assert entries[1].management_request_id == "update-enterprise-memory-a"
        assert entries[1].revoke_request_id == "revoke-enterprise-memory-a"
        assert entries[1].revoke_payload_fingerprint.startswith("hmac-sha256:")


def test_department_memory_api_requires_stable_department_id() -> None:
    client, _ = _build_client()
    invalid = client.post(
        "/api/v1/expense-application-memories/organization",
        headers=_headers(),
        json={
            "scope_type": "department",
            "scope_id": "财务部",
            "value": "火车",
            "expires_in_days": 90,
            "reason": "部门差旅基线",
            "request_id": "create-department-invalid",
        },
    )
    assert invalid.status_code == 422

    created = client.post(
        "/api/v1/expense-application-memories/organization",
        headers=_headers(),
        json={
            "scope_type": "department",
            "scope_id": "department-finance",
            "value": "火车",
            "expires_in_days": 90,
            "reason": "部门差旅基线",
            "request_id": "create-department-finance",
        },
    )
    assert created.status_code == 201, created.text
    assert created.json()["scope_id"] == "department-finance"
    assert "财务部" in created.json()["scope_label"]
