use crate::AedbInstance;
use crate::catalog::DdlOperation;
use crate::catalog::schema::ColumnDef;
use crate::catalog::types::{ColumnType, Row, Value};
use crate::commit::tx::{ReadAssertion, ReadSet, TransactionEnvelope, WriteClass, WriteIntent};
use crate::commit::validation::Mutation;
use crate::config::AedbConfig;
use crate::error::AedbError;
use crate::sync_bridge::AedbSync;
use std::sync::Arc;
use tempfile::tempdir;
use tokio::runtime::Runtime;

#[test]
fn sync_bridge_supports_envelope_assertions() {
    let rt = Runtime::new().expect("runtime");
    let dir = tempdir().expect("tempdir");
    let db = {
        let _guard = rt.enter();
        Arc::new(AedbInstance::open_anonymous(AedbConfig::default(), dir.path()).expect("open"))
    };
    let sync = AedbSync::new(Arc::clone(&db), rt.handle().clone());

    sync.commit_ddl(DdlOperation::CreateProject {
        owner_id: None,
        project_id: "p".into(),
        if_not_exists: true,
    })
    .expect("create project");
    sync.commit_ddl(DdlOperation::CreateScope {
        owner_id: None,
        project_id: "p".into(),
        scope_id: "app".into(),
        if_not_exists: true,
    })
    .expect("create scope");
    sync.commit_ddl(DdlOperation::CreateTable {
        owner_id: None,
        project_id: "p".into(),
        scope_id: "app".into(),
        table_name: "users".into(),
        columns: vec![
            ColumnDef {
                name: "id".into(),
                col_type: ColumnType::Text,
                nullable: false,
            },
            ColumnDef {
                name: "name".into(),
                col_type: ColumnType::Text,
                nullable: false,
            },
        ],
        primary_key: vec!["id".into()],
        if_not_exists: false,
    })
    .expect("create table");

    let seed = sync
        .commit(Mutation::Upsert {
            project_id: "p".into(),
            scope_id: "app".into(),
            table_name: "users".into(),
            primary_key: vec![Value::Text("u1".into())],
            row: Row {
                values: vec![Value::Text("u1".into()), Value::Text("alice".into())],
            },
        })
        .expect("seed");

    let base_seq = seed.commit_seq;
    let ok = sync
        .commit_envelope(TransactionEnvelope {
            caller: None,
            idempotency_key: None,
            write_class: WriteClass::Standard,
            assertions: vec![ReadAssertion::RowVersion {
                project_id: "p".into(),
                scope_id: "app".into(),
                table_name: "users".into(),
                primary_key: vec![Value::Text("u1".into())],
                expected_seq: base_seq,
            }],
            read_set: ReadSet::default(),
            write_intent: WriteIntent {
                mutations: vec![Mutation::Upsert {
                    project_id: "p".into(),
                    scope_id: "app".into(),
                    table_name: "users".into(),
                    primary_key: vec![Value::Text("u1".into())],
                    row: Row {
                        values: vec![Value::Text("u1".into()), Value::Text("alice2".into())],
                    },
                }],
            },
            base_seq,
        })
        .expect("envelope commit");
    assert!(ok.commit_seq > base_seq);

    let stale = sync.commit_envelope(TransactionEnvelope {
        caller: None,
        idempotency_key: None,
        write_class: WriteClass::Standard,
        assertions: vec![ReadAssertion::RowVersion {
            project_id: "p".into(),
            scope_id: "app".into(),
            table_name: "users".into(),
            primary_key: vec![Value::Text("u1".into())],
            expected_seq: base_seq,
        }],
        read_set: ReadSet::default(),
        write_intent: WriteIntent {
            mutations: vec![Mutation::Upsert {
                project_id: "p".into(),
                scope_id: "app".into(),
                table_name: "users".into(),
                primary_key: vec![Value::Text("u1".into())],
                row: Row {
                    values: vec![Value::Text("u1".into()), Value::Text("alice3".into())],
                },
            }],
        },
        base_seq,
    });
    assert!(matches!(stale, Err(AedbError::AssertionFailed { .. })));
}
