"""Append-only evidence and one-way supersession guards."""

from __future__ import annotations

import pytest
from sqlalchemy import Connection, text
from sqlalchemy.exc import DBAPIError

from conftest import add_snapshot


def _run(db: Connection) -> int:
    return db.execute(
        text(
            "INSERT INTO osm_lead_source.reconciliation_run "
            "(region_id, code_version, configuration_hash) VALUES ('test-region', 'test', :hash) RETURNING id"
        ),
        {"hash": "d" * 64},
    ).scalar_one()


@pytest.mark.database
@pytest.mark.integration
def test_observation_candidate_review_and_report_are_fully_immutable(
    db: Connection, seed: dict[str, int]
) -> None:
    db.execute(
        text(
            "INSERT INTO osm_lead_source.source_snapshot_object "
            "(source_snapshot_id, osm_type, osm_id, object_version, source_payload_hash) "
            "VALUES (:snapshot, 'node', 123, 1, :hash)"
        ),
        {"snapshot": seed["snapshot"], "hash": "e" * 64},
    )
    run_id = _run(db)
    candidate = db.execute(
        text(
            "INSERT INTO osm_lead_source.adoption_candidate "
            "(noco_table_id, noco_record_id, result_status, evidence_json, run_id) "
            "VALUES ('scraper', 91, 'needs_review', jsonb_build_object('why', 'check'), :run) RETURNING id"
        ),
        {"run": run_id},
    ).scalar_one()
    review = db.execute(
        text(
            "INSERT INTO osm_lead_source.adoption_candidate_review "
            "(candidate_id, review_outcome, reviewed_by, review_evidence_json) "
            "VALUES (:candidate, 'needs_review', 'test', jsonb_build_object('reason', 'later')) RETURNING id"
        ),
        {"candidate": candidate},
    ).scalar_one()
    report = db.execute(
        text(
            "INSERT INTO osm_lead_source.reconciliation_report "
            "(run_id, report_hash, report_json) VALUES (:run, :hash, jsonb_build_object('ok', true)) "
            "RETURNING id"
        ),
        {"run": run_id, "hash": "f" * 64},
    ).scalar_one()
    statements = [
        ("UPDATE osm_lead_source.source_snapshot_object SET object_version = 2", None),
        ("UPDATE osm_lead_source.adoption_candidate SET result_status = 'accepted'", None),
        (
            "UPDATE osm_lead_source.adoption_candidate_review SET reviewed_by = 'edited'",
            None,
        ),
        ("UPDATE osm_lead_source.reconciliation_report SET report_json = '{}'", None),
        ("DELETE FROM osm_lead_source.source_snapshot_object", None),
        ("DELETE FROM osm_lead_source.adoption_candidate", None),
        ("DELETE FROM osm_lead_source.adoption_candidate_review", None),
        ("DELETE FROM osm_lead_source.reconciliation_report", None),
    ]
    del review, report
    for statement, params in statements:
        with pytest.raises(DBAPIError):
            with db.begin_nested():
                db.execute(text(statement), params)


@pytest.mark.database
@pytest.mark.integration
def test_source_snapshot_attempts_are_finalized_append_only(
    db: Connection, seed: dict[str, int]
) -> None:
    complete = add_snapshot(db, age_days=1)
    statements = [
        "UPDATE osm_lead_source.source_snapshot SET completeness_status = 'partial' WHERE id = :id",
        "UPDATE osm_lead_source.source_snapshot SET downloaded_at = CURRENT_TIMESTAMP WHERE id = :id",
        "UPDATE osm_lead_source.source_snapshot SET content_sha256 = repeat('a', 64) WHERE id = :id",
        "UPDATE osm_lead_source.source_snapshot SET size_bytes = 2 WHERE id = :id",
        "DELETE FROM osm_lead_source.source_snapshot WHERE id = :id",
    ]
    for statement in statements:
        with pytest.raises(DBAPIError):
            with db.begin_nested():
                db.execute(text(statement), {"id": complete})

    partial = add_snapshot(db, status="partial")
    with pytest.raises(DBAPIError):
        with db.begin_nested():
            db.execute(
                text(
                    "UPDATE osm_lead_source.source_snapshot "
                    "SET completeness_status = 'complete' WHERE id = :id"
                ),
                {"id": partial},
            )
    failed = add_snapshot(db, status="failed")
    with pytest.raises(DBAPIError):
        with db.begin_nested():
            db.execute(
                text(
                    "UPDATE osm_lead_source.source_snapshot "
                    "SET completeness_status = 'complete' WHERE id = :id"
                ),
                {"id": failed},
            )
    del seed


@pytest.mark.database
@pytest.mark.integration
def test_tombstone_only_allows_one_way_supersession(db: Connection, seed: dict[str, int]) -> None:
    run_id = _run(db)
    tombstone = db.execute(
        text(
            "INSERT INTO osm_lead_source.osm_lead_tombstone "
            "(noco_table_id, previous_noco_record_id, deleted_at, deleted_by_service_run_id, "
            "deletion_reason, prior_osm_identities, normalized_business_identity, "
            "normalized_business_identity_hash, source_payload_hash, safety_evidence_json) "
            "VALUES ('scraper', 7, CURRENT_TIMESTAMP, :run, 'future evidence', "
            "jsonb_build_object('node', 123), jsonb_build_object('name', 'test'), 'identity', :hash, "
            "jsonb_build_object('safe', false)) RETURNING id"
        ),
        {"run": run_id, "hash": "1" * 64},
    ).scalar_one()
    with pytest.raises(DBAPIError):
        with db.begin_nested():
            db.execute(
                text("UPDATE osm_lead_source.osm_lead_tombstone SET last_known_name = 'edited'")
            )
    db.execute(
        text(
            "UPDATE osm_lead_source.osm_lead_tombstone "
            "SET superseded_at = CURRENT_TIMESTAMP + INTERVAL '1 day', superseded_reason = 'reviewed' "
            "WHERE id = :id"
        ),
        {"id": tombstone},
    )
    with pytest.raises(DBAPIError):
        with db.begin_nested():
            db.execute(
                text(
                    "UPDATE osm_lead_source.osm_lead_tombstone "
                    "SET superseded_at = NULL, superseded_reason = NULL WHERE id = :id"
                ),
                {"id": tombstone},
            )
    del seed
