"""eid_* append-only 구조강제 + 파생뷰 PG 통합 테스트 (W3, review #1).

설계 불변식 #8 의 load-bearing 부분 = DB 강제. 단일 owner role pkm 이라 REVOKE 무효 +
migration 검증기가 plpgsql BEGIN 거부 → RULE(DO INSTEAD NOTHING) + NOT NULL 스탬프로 강제.
순수함수 테스트(test_compose/test_weakness_compute)로는 검증 불가한 'DB 가 실제로 막는가'를 본다.

★ 실행 환경: Postgres(Docker 스택, migrations 301-305 적용 후) 필요 — MacBook 로컬엔 PG 없어
   hard-fail(skip 아님). test_worker_jobs_smoke.py 와 동일 idiom(_worker_pool_helpers).
   staging(devsbx/개발서버 배포 후)에서 가동. 트랜잭션 rollback 으로 테스트 행 오염 0.
"""

from __future__ import annotations

import os
import sys
from pathlib import Path

import pytest
import pytest_asyncio

sys.path.insert(0, str(Path(__file__).resolve().parents[2] / "app"))
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))  # tests/ (helpers)

from sqlalchemy import text  # noqa: E402
from sqlalchemy.exc import IntegrityError  # noqa: E402
from sqlalchemy.ext.asyncio import async_sessionmaker, create_async_engine  # noqa: E402

from _worker_pool_helpers import ensure_user, get_database_url  # noqa: E402

_VALID_INSERT = (
    "INSERT INTO eid_study_weakness "
    "(user_id, weaknesses, habit_signals, trend_label, actor, source_generated_at) "
    "VALUES (:u, '[]'::jsonb, '{}'::jsonb, '악화', 'eid', now()) RETURNING id"
)


@pytest_asyncio.fixture
async def uid():
    return await ensure_user("test-eid-append-only")


@pytest.mark.asyncio
async def test_unstamped_insert_rejected(uid):
    """actor 스탬프 누락 INSERT → NOT NULL 위반 (owner 도 적용 — 스탬프 없는 행 거부)."""
    engine = create_async_engine(get_database_url())
    sm = async_sessionmaker(engine, expire_on_commit=False)
    try:
        with pytest.raises(IntegrityError):
            async with sm() as s:
                await s.execute(
                    text(
                        "INSERT INTO eid_study_weakness "
                        "(user_id, weaknesses, habit_signals, trend_label, source_generated_at) "
                        "VALUES (:u, '[]'::jsonb, '{}'::jsonb, '정체', now())"  # actor 누락
                    ),
                    {"u": uid},
                )
                await s.commit()
    finally:
        await engine.dispose()


@pytest.mark.asyncio
async def test_update_and_delete_are_no_op(uid):
    """RULE DO INSTEAD NOTHING — owner pkm 의 UPDATE/DELETE 도 행을 못 바꾼다(append-only)."""
    engine = create_async_engine(get_database_url())
    sm = async_sessionmaker(engine, expire_on_commit=False)
    try:
        async with sm() as s:
            wid = (await s.execute(text(_VALID_INSERT), {"u": uid})).scalar_one()
            await s.flush()  # 같은 트랜잭션 내 가시 (commit 안 함 → 끝에 rollback 으로 오염 0)

            await s.execute(
                text("UPDATE eid_study_weakness SET trend_label='개선' WHERE id=:i"), {"i": wid}
            )
            tl = (
                await s.execute(text("SELECT trend_label FROM eid_study_weakness WHERE id=:i"), {"i": wid})
            ).scalar_one()
            assert tl == "악화", "UPDATE 가 값을 바꿈 — RULE 미적용(append-only 깨짐)"

            await s.execute(text("DELETE FROM eid_study_weakness WHERE id=:i"), {"i": wid})
            cnt = (
                await s.execute(text("SELECT count(*) FROM eid_study_weakness WHERE id=:i"), {"i": wid})
            ).scalar_one()
            assert cnt == 1, "DELETE 가 행을 지움 — RULE 미적용(append-only 깨짐)"

            await s.rollback()  # 테스트 행 폐기
    finally:
        await engine.dispose()


@pytest.mark.asyncio
async def test_schedule_views_queryable(uid):
    """v_schedule_today / v_schedule_defer_pattern 정의 유효성 smoke (enum 리터럴·LATERAL·date_trunc).

    뷰가 invalid 면 CREATE 시점 또는 SELECT 시점에 에러 → 쿼리 성공 = DDL 유효.
    """
    engine = create_async_engine(get_database_url())
    sm = async_sessionmaker(engine, expire_on_commit=False)
    try:
        async with sm() as s:
            await s.execute(text("SELECT * FROM v_schedule_today LIMIT 1"))
            await s.execute(text("SELECT * FROM v_schedule_defer_pattern LIMIT 1"))
    finally:
        await engine.dispose()
