use automerge::{AutomergeError, ROOT, ReadDoc, transaction::Transactable};
use samod_core::{
    BackoffConfig, DialerConfig, DocSearchPhase, PeerRequestState, network::ConnectionEvent,
};
use samod_test_harness::{Connected, Network, RunningDocIds};

fn init_logging() {
    let _ = tracing_subscriber::fmt()
        .with_env_filter(tracing_subscriber::EnvFilter::from_default_env())
        .try_init();
}

#[test]
fn find_after_create() {
    init_logging();

    let mut network = Network::new();

    let alice = network.create_samod("Alice_Original");
    // Alice creates a document
    let RunningDocIds { doc_id, actor_id } = network.samod(&alice).create_document();
    // Add data to Alice's document
    network
        .samod(&alice)
        .with_document_by_actor(actor_id, |doc| {
            let mut tx = doc.transaction();
            tx.put(automerge::ROOT, "foo", "bar").unwrap();
            tx.commit();
        })
        .expect("with_document should succeed");

    network.run_until_quiescent();

    let alice_storage = network.samod(&alice).storage().clone();
    let alice2 = network.create_samod_with_storage("Alice_Second", alice_storage);
    // now, run find again
    let actor_id2 = network.samod(&alice2).find_document(&doc_id).unwrap();

    let result = {
        network
            .samod(&alice2)
            .with_document_by_actor(actor_id2, |doc| {
                doc.get(automerge::ROOT, "foo")
                    .unwrap()
                    .map(|(value, _)| match value {
                        automerge::Value::Scalar(s) => match s.as_ref() {
                            automerge::ScalarValue::Str(string) => string.to_string(),
                            _ => s.to_string(),
                        },
                        _ => value.to_string(),
                    })
                    .unwrap_or_default()
            })
            .expect("with_document should succeed")
    };
    assert_eq!(result, "bar");
}

#[test]
fn three_peer_chain_sync() {
    init_logging();

    let mut network = Network::new();

    // Create three peers: Alice, Bob, and Charlie
    let alice = network.create_samod("Alice");
    let bob = network.create_samod("Bob");
    let charlie = network.create_samod("Charlie");

    // Connect them in a chain: Alice <-> Bob <-> Charlie
    network.connect(alice, bob);
    network.connect(bob, charlie);

    // Run until handshakes complete
    network.run_until_quiescent();

    // Verify all handshakes completed
    for (name, peer_id) in [("Alice", alice), ("Bob", bob), ("Charlie", charlie)] {
        let events = network.samod(&peer_id).connection_events();
        let handshake_completed = events
            .iter()
            .any(|event| matches!(event, ConnectionEvent::HandshakeCompleted { .. }));
        assert!(handshake_completed, "{name}'s handshake should complete");
    }

    // Charlie creates a document
    let RunningDocIds { doc_id, actor_id } = network.samod(&charlie).create_document();

    // Add a change to Charlie's document
    let result = network
        .samod(&charlie)
        .with_document_by_actor(actor_id, |doc| {
            let mut tx = doc.transaction();
            tx.put(automerge::ROOT, "creator", "charlie").unwrap();
            tx.put(automerge::ROOT, "message", "hello from charlie")
                .unwrap();
            tx.commit();
            "change_applied"
        })
        .unwrap();

    assert_eq!(result, "change_applied");

    // Run here so that charlie has a chance to announce to Bob. Otherwise what can happen
    // is that the request from bob and the request from alice cross in the air and so
    // alice considers the document unavailable (because she doesn't have it in storage,
    // and is only connected to bob, who just requested it from her)
    network.run_until_quiescent();

    let alice_actor_id = network.samod(&alice).find_document(&doc_id);

    // Verify Alice found the document through the chain
    assert!(
        alice_actor_id.is_some(),
        "Alice should find the document through Bob from Charlie"
    );
    let alice_actor_id = alice_actor_id.unwrap();

    // Verify Alice's document contains the expected data
    let verification_result = network
        .samod(&alice)
        .with_document_by_actor(alice_actor_id, |doc| {
            // Check the document content
            let creator = doc
                .get(automerge::ROOT, "creator")
                .unwrap()
                .map(|(value, _)| value.into_string().unwrap())
                .unwrap_or_default();
            let message = doc
                .get(automerge::ROOT, "message")
                .unwrap()
                .map(|(value, _)| value.into_string().unwrap())
                .unwrap_or_default();

            (creator, message)
        })
        .expect("with_document should succeed");

    assert_eq!(verification_result.0, "charlie");
    assert_eq!(verification_result.1, "hello from charlie");

    let bob_actor_id = network.samod(&bob).find_document(&doc_id);

    assert!(bob_actor_id.is_some(), "Bob should also have the document");
}

#[test]
fn document_persistence_across_restart() {
    init_logging();

    let mut network = Network::new();

    // Create Alice and Bob (but don't connect them yet)
    let alice_original = network.create_samod("Alice_Original");
    let bob = network.create_samod("Bob");

    // Alice creates a document
    let RunningDocIds { doc_id, actor_id } = network.samod(&alice_original).create_document();

    // Add data to Alice's document
    let result = {
        network
            .samod(&alice_original)
            .with_document_by_actor(actor_id, |doc| {
                let mut tx = doc.transaction();
                tx.put(automerge::ROOT, "persistent_data", "survives_restart")
                    .unwrap();
                tx.commit();
                "data_added"
            })
            .expect("with_document should succeed")
    };

    assert_eq!(result, "data_added");

    // Get Alice's storage to simulate restart
    let alice_storage = network.samod(&alice_original).storage().clone();

    // Create a new Alice instance with the same storage (simulating restart)
    let alice_restarted = network.create_samod_with_storage("Alice_Restarted", alice_storage);

    // Now connect the restarted Alice to Bob
    network.connect(alice_restarted, bob);
    network.run_until_quiescent();

    // Verify handshake completed
    let alice_events = network.samod(&alice_restarted).connection_events();
    let bob_events = network.samod(&bob).connection_events();

    let alice_handshake_completed = alice_events
        .iter()
        .any(|event| matches!(event, ConnectionEvent::HandshakeCompleted { .. }));
    let bob_handshake_completed = bob_events
        .iter()
        .any(|event| matches!(event, ConnectionEvent::HandshakeCompleted { .. }));

    assert!(
        alice_handshake_completed,
        "Restarted Alice's handshake should complete"
    );
    assert!(bob_handshake_completed, "Bob's handshake should complete");

    let bob_actor_id = network
        .samod(&bob)
        .find_document(&doc_id)
        .expect("document should be found on bob");

    // Verify the document data persisted correctly
    let verification_result = network
        .samod(&bob)
        .with_document_by_actor(bob_actor_id, |doc| {
            println!("🔍 Bob's document keys: {:?}", doc.keys(automerge::ROOT));

            doc.get(automerge::ROOT, "persistent_data")
                .unwrap()
                .map(|(value, _)| match value {
                    automerge::Value::Scalar(s) => match s.as_ref() {
                        automerge::ScalarValue::Str(string) => string.to_string(),
                        _ => s.to_string(),
                    },
                    _ => value.to_string(),
                })
                .unwrap_or_default()
        })
        .expect("with_document should succeed");

    assert_eq!(verification_result, "survives_restart");
}

#[test]
fn unavailable_document_multiple_peers() {
    init_logging();

    let mut network = Network::new();

    // Create three peers and connect them all to each other
    let alice = network.create_samod("Alice");
    let bob = network.create_samod("Bob");
    let charlie = network.create_samod("Charlie");

    network.connect(alice, bob);
    network.connect(alice, charlie);
    network.connect(bob, charlie);

    // Run until handshakes complete
    network.run_until_quiescent();
    println!("✅ All three peers connected to each other");

    // Request a non-existent document from Alice
    let fake_doc_id = samod_core::DocumentId::new(&mut rand::rng());
    let alice_result = network.samod(&alice).find_document(&fake_doc_id);

    assert!(alice_result.is_none(), "Document should not be found");
}

#[test]
fn unavailable_document_single_peer() {
    init_logging();

    let mut network = Network::new();

    // Create a single peer
    let alice = network.create_samod("Alice");

    // Request a non-existent document
    let fake_doc_id = samod_core::DocumentId::new(&mut rand::rng());
    let alice_result = network.samod(&alice).find_document(&fake_doc_id);

    assert!(alice_result.is_none(), "Document should not be found");
}

#[test]
fn request_document_before_connection() {
    init_logging();

    let mut network = Network::new();

    // Create Alice and Bob but don't connect them yet
    let alice = network.create_samod("Alice");
    let bob = network.create_samod("Bob");

    // Alice creates a document
    let RunningDocIds { doc_id, actor_id } = network.samod(&alice).create_document();

    // Add data to Alice's document
    let result = network
        .samod(&alice)
        .with_document_by_actor(actor_id, |doc| {
            let mut tx = doc.transaction();
            tx.put(
                automerge::ROOT,
                "delayed_sync",
                "should_work_after_connection",
            )
            .unwrap();
            tx.commit();
            "data_added"
        })
        .expect("with document should succeed");

    assert_eq!(result, "data_added");

    // Bob requests the document before being connected to Alice
    let bob_result = network.samod(&bob).find_document(&doc_id);
    assert!(
        bob_result.is_none(),
        "document should not be found before connection"
    );

    // Now connect Alice and Bob
    network.connect(alice, bob);
    network.run_until_quiescent();

    // Verify handshake completed
    let alice_events = network.samod(&alice).connection_events();
    let bob_events = network.samod(&bob).connection_events();
    let alice_handshake_completed = alice_events
        .iter()
        .any(|event| matches!(event, ConnectionEvent::HandshakeCompleted { .. }));
    let bob_handshake_completed = bob_events
        .iter()
        .any(|event| matches!(event, ConnectionEvent::HandshakeCompleted { .. }));
    assert!(
        alice_handshake_completed,
        "Alice's handshake should complete"
    );
    assert!(bob_handshake_completed, "Bob's handshake should complete");

    // Now find again
    let bob_actor_id = network
        .samod(&bob)
        .find_document(&doc_id)
        .expect("document should be found on bob after connection");

    // Verify the document data
    let verification_result = network
        .samod(&bob)
        .with_document_by_actor(bob_actor_id, |doc| {
            doc.get(automerge::ROOT, "delayed_sync")
                .unwrap()
                .map(|(value, _)| match value {
                    automerge::Value::Scalar(s) => match s.as_ref() {
                        automerge::ScalarValue::Str(string) => string.to_string(),
                        _ => s.to_string(),
                    },
                    _ => value.to_string(),
                })
                .unwrap_or_default()
        })
        .expect("with_document should succeed");

    assert_eq!(verification_result, "should_work_after_connection");
}

#[test]
fn peer_with_announce_policy_set_to_true_should_announce_to_peers() {
    init_logging();

    let mut network = Network::new();

    let alice = network.create_samod("Alice");
    let bob = network.create_samod("Bob");

    network.connect(alice, bob);
    network.run_until_quiescent();

    // Now create a documnt on alice
    let RunningDocIds { doc_id, actor_id } = network.samod(&alice).create_document();
    network
        .samod(&alice)
        .with_document_by_actor(actor_id, |doc| {
            doc.transact::<_, _, AutomergeError>(|tx| {
                tx.put(automerge::ROOT, "foo", "bar")?;
                Ok(())
            })
            .unwrap()
        })
        .unwrap();

    network.run_until_quiescent();

    // Now disconnect the two peers
    network.disconnect(alice, bob);

    // Now, attempting to find the document on bob should succeed because Alice was set
    // to announce the document to this peer
    assert!(network.samod(&bob).find_document(&doc_id).is_some());
}

#[test]
fn peer_with_announce_policy_set_to_false_does_not_announce() {
    init_logging();

    let mut network = Network::new();

    let alice = network.create_samod("Alice");
    network
        .samod(&alice)
        .set_announce_policy(Box::new(|_doc_id, _peer_id| false));
    let bob = network.create_samod("Bob");

    network.connect(alice, bob);
    network.run_until_quiescent();

    // Now create a documnt on alice
    let RunningDocIds { doc_id, actor_id } = network.samod(&alice).create_document();
    network
        .samod(&alice)
        .with_document_by_actor(actor_id, |doc| {
            doc.transact::<_, _, AutomergeError>(|tx| {
                tx.put(automerge::ROOT, "foo", "bar")?;
                Ok(())
            })
            .unwrap()
        })
        .unwrap();

    network.run_until_quiescent();

    // Now disconnect the two peers
    network.disconnect(alice, bob);

    // Now, attempting to find the document on bob should fail because Alice was set
    // to not announce the document
    assert!(network.samod(&bob).find_document(&doc_id).is_none());
}

#[test]
fn peer_with_announce_policy_set_to_false_does_not_request() {
    init_logging();

    let mut network = Network::new();

    let alice = network.create_samod("Alice");
    network
        .samod(&alice)
        .set_announce_policy(Box::new(|_doc_id, _peer_id| false));
    let bob = network.create_samod("Bob");
    network
        .samod(&bob)
        .set_announce_policy(Box::new(|_, _| false));

    network.connect(alice, bob);
    network.run_until_quiescent();

    // Now create a documnt on alice
    let RunningDocIds { doc_id, actor_id } = network.samod(&alice).create_document();
    network
        .samod(&alice)
        .with_document_by_actor(actor_id, |doc| {
            doc.transact::<_, _, AutomergeError>(|tx| {
                tx.put(automerge::ROOT, "foo", "bar")?;
                Ok(())
            })
            .unwrap()
        })
        .unwrap();

    network.run_until_quiescent();

    // Now, attempting to find the document on bob should fail because bob is set
    // to not announce the document and requesting from Alice is equivalent to
    // announcing
    assert!(network.samod(&bob).find_document(&doc_id).is_none());
}

#[test]
fn sync_while_requesting() {
    // Say we have three peers connected like this:
    //
    // alice <-> bob <-> charlie <-> derek
    //
    // Alice is configured to announce everything to bob. The scenario this
    // tests exercises is when Alice creates a document and Derek searches
    // for it before the sync from Alice to Bob to Charlie has completed. There
    // was a bug where the request handling logic meant that Derek would not
    // find the document. The buggy request logic was something like this:
    //
    // "When you receive a sync message for a document not in storage, send
    // a request to every peer you are connected to for the document"
    //
    // This resulted in this sequence of events:
    //
    // * Alice announces document to bob
    // * Bob begins syncing with Alice
    // * Bob also sends a request to Charlie for the document
    // * The `find` call is issued to Derek
    // * Derek sends a request to Charlie
    //
    // At this point Charlie has received a request from Bob and a request from
    // Derek, from Charlies perspective no-one has the document so he returns a
    // not-available response to Derek.

    init_logging();
    let mut network = Network::new();
    let alice = network.create_samod("alice");
    let bob = network.create_samod("bob");
    let charlie = network.create_samod("charlie");
    let derek = network.create_samod("derek");

    network.connect(alice, bob);
    network.connect(bob, charlie);
    network.connect(charlie, derek);

    network.run_until_quiescent();

    // Create the document on alice
    let RunningDocIds { doc_id, .. } = network.samod(&alice).create_document();

    network.samod(&derek).search_for_doc(&doc_id);

    network.run_until_quiescent();

    assert!(
        network.samod(&derek).is_document_available(&doc_id),
        "document should eventually be available on derek"
    )
}

#[test]
fn create_while_connected() {
    init_logging();
    let mut network = Network::new();
    let alice = network.create_samod("alice");
    let bob = network.create_samod("bob");

    network.connect(alice, bob);

    network.run_until_quiescent();

    // Create the document on alice
    let RunningDocIds { doc_id, .. } = network.samod(&alice).create_document();

    // Now modify the document on alice
    network
        .samod(&alice)
        .with_document(&doc_id, |doc| {
            doc.transact::<_, _, AutomergeError>(|tx| {
                tx.put(ROOT, "foo", "bar")?;
                Ok(())
            })
            .unwrap();
        })
        .unwrap();

    let alice_peer_id = network.samod(&alice).peer_id();
    let bob_peer_id = network.samod(&bob).peer_id();
    network
        .run_until_message_received_at(alice_peer_id, bob_peer_id)
        .unwrap();

    network.samod(&bob).search_for_doc(&doc_id);

    network.run_until_quiescent();

    assert!(
        network.samod(&bob).is_document_available(&doc_id),
        "bob should have the doc"
    );
}

#[test]
fn three_chained_sync_servers() {
    init_logging();

    let mut network = Network::new();

    // Create three peers: Alice, Bob, and Charlie
    // Then set their announce policies so that alice never announces, bob only
    // announces to alice, and charlie only announces to bob. This simulates a
    // chain of federating sync servers
    let alice = network.create_samod("Alice");
    network
        .samod(&alice)
        .set_announce_policy(Box::new(|_, _| false));
    let alice_peer_id = network.samod(&alice).peer_id();
    let bob = network.create_samod("Bob");
    network
        .samod(&bob)
        .set_announce_policy(Box::new(move |_, peer_id| peer_id == alice_peer_id));
    let bob_peer_id = network.samod(&bob).peer_id();
    let charlie = network.create_samod("Charlie");
    network
        .samod(&charlie)
        .set_announce_policy(Box::new(move |_, peer_id| peer_id == bob_peer_id));

    // Connect them in a chain: Alice <-> Bob <-> Charlie
    network.connect(alice, bob);
    network.connect(bob, charlie);

    // Run until handshakes complete
    network.run_until_quiescent();

    // Verify all handshakes completed
    for (name, peer_id) in [("Alice", alice), ("Bob", bob), ("Charlie", charlie)] {
        let events = network.samod(&peer_id).connection_events();
        let handshake_completed = events
            .iter()
            .any(|event| matches!(event, ConnectionEvent::HandshakeCompleted { .. }));
        assert!(handshake_completed, "{name}'s handshake should complete");
    }

    // Alice creates a document
    let RunningDocIds { doc_id, actor_id } = network.samod(&alice).create_document();

    // Add a change to Alice's document
    let result = network
        .samod(&alice)
        .with_document_by_actor(actor_id, |doc| {
            let mut tx = doc.transaction();
            tx.put(automerge::ROOT, "creator", "alice").unwrap();
            tx.put(automerge::ROOT, "message", "hello from alice")
                .unwrap();
            tx.commit();
            "change_applied"
        })
        .unwrap();

    assert_eq!(result, "change_applied");

    network.run_until_quiescent();

    let charlie_actor_id = network.samod(&charlie).find_document(&doc_id);

    // Verify Alice found the document through the chain
    assert!(
        charlie_actor_id.is_some(),
        "Charlie should find the document through Bob from Charlie"
    );
    let charlie_actor_id = charlie_actor_id.unwrap();

    // Verify Charlie's document contains the expected data
    let verification_result = network
        .samod(&charlie)
        .with_document_by_actor(charlie_actor_id, |doc| {
            // Check the document content
            let creator = doc
                .get(automerge::ROOT, "creator")
                .unwrap()
                .map(|(value, _)| value.into_string().unwrap())
                .unwrap_or_default();
            let message = doc
                .get(automerge::ROOT, "message")
                .unwrap()
                .map(|(value, _)| value.into_string().unwrap())
                .unwrap_or_default();

            (creator, message)
        })
        .expect("with_document should succeed");

    assert_eq!(verification_result.0, "alice");
    assert_eq!(verification_result.1, "hello from alice");
}

#[test]
fn dont_announce_policy_retains_documents_synced_by_clients() {
    // This test is a reproduction of an issue described in
    // https://github.com/alexjg/samod/pull/85
    //
    // The issue was that if we receive a sync message for a document we don't
    // have from a peer who does have the document, but our announce policy is
    // set to false, then we erroneously treat the document as unavailable. The
    // reason for this is that we were dropping the sync message from the peer
    // because of the announce policy, but the sync message contained the
    // document.
    init_logging();
    let mut network = Network::new();

    let server = network.create_samod("Server");
    network
        .samod(&server)
        .set_announce_policy(Box::new(|_, _| false));

    let client = network.create_samod("Client");

    let RunningDocIds { doc_id, actor_id } = network.samod(&client).create_document();
    network
        .samod(&client)
        .with_document_by_actor(actor_id, |doc| {
            doc.transact::<_, _, AutomergeError>(|tx| {
                tx.put(automerge::ROOT, "foo", "bar")?;
                Ok(())
            })
            .unwrap()
        })
        .unwrap();

    network.run_until_quiescent();

    network.connect(client, server);
    network.run_until_quiescent();

    let server_actor = network.samod(&server).find_document(&doc_id);
    assert!(server_actor.is_some(), "Server should have the document");

    let server_actor = server_actor.unwrap();
    let val = network
        .samod(&server)
        .with_document_by_actor(server_actor, |doc| {
            doc.get(automerge::ROOT, "foo")
                .unwrap()
                .map(|(v, _)| v.to_string())
        })
        .unwrap();

    assert_eq!(val.as_deref(), Some("\"bar\""));
}

#[test]
fn find_doesnt_bounce_through_unavailable_when_receiving_doc() {
    init_logging();
    let mut network = Network::new();

    let server = network.create_samod("Server");
    network
        .samod(&server)
        .set_announce_policy(Box::new(|_, _| false));

    let client = network.create_samod("Client");

    network.connect(client, server);
    network.run_until_quiescent();

    // Peers are now connected, now create the document on the client whilst
    // simultaenously finding it on the server

    let RunningDocIds { doc_id, actor_id } = network.samod(&client).create_document();
    network
        .samod(&client)
        .with_document_by_actor(actor_id, |doc| {
            doc.transact::<_, _, AutomergeError>(|tx| {
                tx.put(automerge::ROOT, "foo", "bar")?;
                Ok(())
            })
            .unwrap()
        })
        .unwrap();
    network.samod(&server).search_for_doc(&doc_id);

    network.samod(&server).pause_storage();
    assert!(!network.samod(&server).is_document_available(&doc_id));
    network.run_until_quiescent();
    network.samod(&server).resume_storage();
    network.run_until_quiescent();

    assert!(
        network.samod(&server).is_document_available(&doc_id),
        "document should be found on server"
    );
}

#[test]
fn search_status_from_requesting_to_ready() {
    init_logging();

    let mut network = Network::new();

    // Create three peers: Alice, Bob, and Charlie
    let alice = network.create_samod("Alice");
    network
        .samod(&alice)
        .set_announce_policy(Box::new(|_, _| false));
    let bob = network.create_samod("Bob");
    let charlie = network.create_samod("Charlie");

    // Connect them in a chain: Alice <-> Bob <-> Charlie
    let Connected {
        left: _,
        right: alice_from_bob,
    } = network.connect(alice, bob);
    let Connected {
        left: charlie_from_bob,
        right: _,
    } = network.connect(bob, charlie);

    network.run_until_quiescent();

    // Create a document on Alice
    let doc_id = network.samod(&alice).create_document().doc_id;
    network
        .samod(&alice)
        .with_document(&doc_id, |doc| {
            doc.transact::<_, _, AutomergeError>(|tx| {
                tx.put(automerge::ROOT, "foo", "bar")?;
                Ok(())
            })
            .unwrap();
        })
        .unwrap();

    network.run_until_quiescent();

    // Begin requesting the document from bob
    network.samod(&bob).search_for_doc(&doc_id);

    // The document should be in a searching state now because [`search_for_doc`] handles the
    // local storage events but doesn't run the network.
    let DocSearchPhase::Searching(peer_states) = network
        .samod(&bob)
        .search_status(&doc_id)
        .expect("there should be a search state")
        .phase()
        .clone()
    else {
        panic!("document should be in searching state");
    };
    println!("{:?}", peer_states);
    assert_eq!(
        peer_states.len(),
        2,
        "peer states should show both peers as requesting"
    );
    assert_eq!(
        peer_states
            .get(&alice_from_bob)
            .expect("alice should be in bobs peer states"),
        &PeerRequestState::Requested,
        "alice should be in requested state"
    );
    assert_eq!(
        peer_states
            .get(&charlie_from_bob)
            .expect("charlie should be in bobs peer states"),
        &PeerRequestState::Requested,
        "charlie should be in requested state"
    );
    assert!(
        !network
            .samod(&bob)
            .search_status(&doc_id)
            .unwrap()
            .is_currently_unavailable(),
        "document should not be currently unavailable whilst we're requesting"
    );

    network.run_until_quiescent();

    // The document should be in a synced state now that the network has run.
    let DocSearchPhase::Ready = network
        .samod(&bob)
        .search_status(&doc_id)
        .expect("there should be a search state")
        .phase()
        .clone()
    else {
        panic!("document should be in synced state");
    };
}

#[test]
fn search_state_not_found_if_no_one_has_doc() {
    init_logging();

    let mut network = Network::new();

    // Create three peers: Alice, Bob, and Charlie
    let alice = network.create_samod("Alice");
    let bob = network.create_samod("Bob");

    let RunningDocIds { doc_id, .. } = network.samod(&alice).create_document();

    network.samod(&bob).search_for_doc(&doc_id);

    network.run_until_quiescent();

    assert!(
        network
            .samod(&bob)
            .search_status(&doc_id)
            .expect("search state should be found")
            .is_currently_unavailable(),
        "search should be in a not currently available state if no one has the document"
    );
}

#[test]
fn search_state_not_unavailable_after_dialer_added() {
    init_logging();

    let mut network = Network::new();

    // Create three peers: Alice, Bob, and Charlie
    let alice = network.create_samod("Alice");
    let bob = network.create_samod("Bob");

    let RunningDocIds { doc_id, .. } = network.samod(&alice).create_document();

    network.samod(&bob).search_for_doc(&doc_id);

    network.run_until_quiescent();

    // Now add a dialer to bob
    network.samod(&bob).add_dialer(DialerConfig {
        url: url::Url::parse("wss://sync.example.com/automerge").unwrap(),
        backoff: BackoffConfig::default(),
    });

    assert!(
        !network
            .samod(&bob)
            .search_status(&doc_id)
            .expect("search state should be found")
            .is_currently_unavailable(),
        "search should not be in a currently unavailable state if a dialer is still connecting"
    );
}
