#![cfg(feature = "serde-seeded")]

use btree_indexmap::{BTreeIndexMap, BTreeIndexMultiMap, BTreeIndexMultiSet, BTreeIndexSet};
use serde::{Deserialize, Serialize};
use serde_seeded::{DeserializeSeeded, SerializeSeeded, ser::Seeded};

/// A dummy seed: values are scaled by this factor on serialization, and
/// divided back on deserialization. This lets tests check that the seed is
/// actually threaded through nested values, rather than merely ignored.
struct Scale(i32);

#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
struct Scaled(i32);

impl SerializeSeeded<Scale> for Scaled {
	fn serialize_seeded<S>(&self, seed: &Scale, serializer: S) -> Result<S::Ok, S::Error>
	where
		S: serde::Serializer,
	{
		(self.0 * seed.0).serialize(serializer)
	}
}

impl<'de> DeserializeSeeded<'de, Scale> for Scaled {
	fn deserialize_seeded<D>(seed: &Scale, deserializer: D) -> Result<Self, D::Error>
	where
		D: serde::Deserializer<'de>,
	{
		let raw = i32::deserialize(deserializer)?;
		Ok(Scaled(raw / seed.0))
	}
}

fn to_string_seeded<T: SerializeSeeded<Scale>>(seed: &Scale, value: &T) -> String {
	serde_json::to_string(&Seeded::new(seed, value)).unwrap()
}

fn from_str_seeded<T: for<'de> DeserializeSeeded<'de, Scale>>(seed: &Scale, s: &str) -> T {
	let mut de = serde_json::Deserializer::from_str(s);
	let value = DeserializeSeeded::deserialize_seeded(seed, &mut de).unwrap();
	de.end().unwrap();
	value
}

// ── BTreeIndexMap ────────────────────────────────────────────────────────────

#[test]
fn map_round_trip_threads_seed() {
	let mut map = BTreeIndexMap::new();
	map.insert("b".to_string(), Scaled(2));
	map.insert("a".to_string(), Scaled(1));

	let seed = Scale(10);
	let json = to_string_seeded(&seed, &map);
	assert_eq!(json, r#"{"b":20,"a":10}"#);

	let round_tripped: BTreeIndexMap<String, Scaled> = from_str_seeded(&seed, &json);
	assert_eq!(map, round_tripped);
}

// ── BTreeIndexMultiMap ───────────────────────────────────────────────────────

#[test]
fn multi_map_round_trip_threads_seed_and_preserves_duplicate_keys() {
	let mut map = BTreeIndexMultiMap::new();
	map.push_back("a".to_string(), Scaled(1));
	map.push_back("a".to_string(), Scaled(2));
	map.push_back("b".to_string(), Scaled(3));

	let seed = Scale(10);
	let json = to_string_seeded(&seed, &map);
	assert_eq!(json, r#"{"a":10,"a":20,"b":30}"#);

	let round_tripped: BTreeIndexMultiMap<String, Scaled> = from_str_seeded(&seed, &json);
	assert_eq!(map, round_tripped);
	assert_eq!(round_tripped.key_occurences("a"), 2);
}

// ── BTreeIndexSet ────────────────────────────────────────────────────────────

#[test]
fn set_round_trip_threads_seed() {
	let mut set = BTreeIndexSet::new();
	set.insert(Scaled(2));
	set.insert(Scaled(1));

	let seed = Scale(10);
	let json = to_string_seeded(&seed, &set);
	assert_eq!(json, "[20,10]");

	let round_tripped: BTreeIndexSet<Scaled> = from_str_seeded(&seed, &json);
	assert_eq!(set, round_tripped);
}

// ── BTreeIndexMultiSet ───────────────────────────────────────────────────────

#[test]
fn multi_set_round_trip_threads_seed_and_preserves_duplicate_values() {
	let mut set = BTreeIndexMultiSet::new();
	set.push_back(Scaled(1));
	set.push_back(Scaled(1));
	set.push_back(Scaled(2));

	let seed = Scale(10);
	let json = to_string_seeded(&seed, &set);
	assert_eq!(json, "[10,10,20]");

	let round_tripped: BTreeIndexMultiSet<Scaled> = from_str_seeded(&seed, &json);
	assert_eq!(set, round_tripped);
	assert_eq!(round_tripped.count(&Scaled(1)), 2);
}

// ── unseeded key helpers ─────────────────────────────────────────────────────

#[test]
fn unseeded_map_key_round_trip() {
	use btree_indexmap::serde_seeded::unseeded_btree_index_map_key;

	let mut map = BTreeIndexMap::new();
	map.insert("b".to_string(), Scaled(2));
	map.insert("a".to_string(), Scaled(1));

	let seed = Scale(10);

	let mut buf = Vec::new();
	unseeded_btree_index_map_key::serialize_seeded(
		&map,
		&seed,
		&mut serde_json::Serializer::new(&mut buf),
	)
	.unwrap();
	let json = String::from_utf8(buf).unwrap();
	assert_eq!(json, r#"{"b":20,"a":10}"#);

	let mut de = serde_json::Deserializer::from_str(&json);
	let round_tripped: BTreeIndexMap<String, Scaled> =
		unseeded_btree_index_map_key::deserialize_seeded(&seed, &mut de).unwrap();
	de.end().unwrap();

	assert_eq!(map, round_tripped);
}

#[test]
fn unseeded_multi_map_key_round_trip() {
	use btree_indexmap::serde_seeded::unseeded_btree_index_multi_map_key;

	let mut map = BTreeIndexMultiMap::new();
	map.push_back("a".to_string(), Scaled(1));
	map.push_back("a".to_string(), Scaled(2));

	let seed = Scale(10);

	let mut buf = Vec::new();
	unseeded_btree_index_multi_map_key::serialize_seeded(
		&map,
		&seed,
		&mut serde_json::Serializer::new(&mut buf),
	)
	.unwrap();
	let json = String::from_utf8(buf).unwrap();
	assert_eq!(json, r#"{"a":10,"a":20}"#);

	let mut de = serde_json::Deserializer::from_str(&json);
	let round_tripped: BTreeIndexMultiMap<String, Scaled> =
		unseeded_btree_index_multi_map_key::deserialize_seeded(&seed, &mut de).unwrap();
	de.end().unwrap();

	assert_eq!(map, round_tripped);
}
