// Integration tests for brand inference across all supported types and operations.
//
// Validates that InferableBrand impls are correctly auto-generated by impl_kind!
// and that inference-based free functions work for all unambiguous types.

use fp_library::{
	brands::*,
	functions::*,
	types::*,
};

// -- Arity-1 InferableBrand: map --

#[test]
fn map_option() {
	assert_eq!(map(|x: i32| x * 2, Some(5)), Some(10));
}

#[test]
fn map_vec() {
	assert_eq!(map(|x: i32| x + 1, vec![1, 2, 3]), vec![2, 3, 4]);
}

#[test]
fn map_identity() {
	assert_eq!(map(|x: i32| x * 3, Identity(7)), Identity(21));
}

#[test]
fn map_tuple1() {
	assert_eq!(map(|x: i32| x + 10, (5,)), (15,));
}

#[test]
fn map_thunk() {
	let t = Thunk::new(|| 5);
	let result = map(|x: i32| x * 2, t);
	assert_eq!(result.evaluate(), 10);
}

// -- Arity-1 InferableBrand: bind --

#[test]
fn bind_option() {
	assert_eq!(bind(Some(5), |x: i32| Some(x * 2)), Some(10));
}

#[test]
fn bind_vec() {
	let result: Vec<i32> = bind(vec![1, 2], |x: i32| vec![x, x * 10]);
	assert_eq!(result, vec![1, 10, 2, 20]);
}

// -- Arity-1 InferableBrand: ref dispatch --

#[test]
fn ref_map_option() {
	let o = Some(5);
	assert_eq!(map(|x: &i32| *x * 2, &o), Some(10));
}

#[test]
fn ref_map_vec() {
	let v = vec![1, 2, 3];
	assert_eq!(map(|x: &i32| *x + 10, &v), vec![11, 12, 13]);
}

#[test]
fn ref_map_rc_lazy() {
	let l = RcLazy::pure(5);
	let result = map(|x: &i32| *x * 2, &l);
	assert_eq!(*result.evaluate(), 10);
}

// -- Arity-1 InferableBrand: other operations --

#[test]
fn filter_map_option() {
	assert_eq!(
		filter_map(|x: i32| if x > 2 { Some(x * 10) } else { None }, vec![1, 2, 3, 4]),
		vec![30, 40]
	);
}

#[test]
fn fold_left_vec() {
	let result = fold_left::<RcFnBrand, _, _, _, _>(|acc: i32, x: i32| acc + x, 0, vec![1, 2, 3]);
	assert_eq!(result, 6);
}

// -- Arity-2 InferableBrand: bimap --

#[test]
fn bimap_result() {
	let x: Result<i32, i32> = Ok(5);
	assert_eq!(bimap((|e| e + 1, |s| s * 2), x), Ok(10));

	let y: Result<i32, i32> = Err(3);
	assert_eq!(bimap((|e| e + 1, |s| s * 2), y), Err(4));
}

#[test]
fn bimap_tuple2() {
	assert_eq!(bimap((|a: i32| a + 1, |b: i32| b * 2), (3, 5)), (4, 10));
}

#[test]
fn bimap_control_flow() {
	use std::ops::ControlFlow;

	// ControlFlowBrand's Of<C, B> = ControlFlow<B, C>
	// so bimap's first function maps C (Continue), second maps B (Break)
	let cf: ControlFlow<i32, i32> = ControlFlow::Continue(5);
	let result = bimap((|c: i32| c * 2, |b: i32| b + 1), cf);
	assert_eq!(result, ControlFlow::Continue(10));

	let cf: ControlFlow<i32, i32> = ControlFlow::Break(3);
	let result = bimap((|c: i32| c * 2, |b: i32| b + 1), cf);
	assert_eq!(result, ControlFlow::Break(4));
}

// -- Arity-2 InferableBrand: ref bimap --

#[test]
fn ref_bimap_result() {
	let x: Result<i32, i32> = Ok(5);
	assert_eq!(bimap((|e: &i32| *e + 1, |s: &i32| *s * 2), &x), Ok(10));
}

// -- Arity-2 InferableBrand: bi_fold_map --

#[test]
fn bi_fold_map_result() {
	let x: Result<i32, i32> = Ok(5);
	let y = bi_fold_map::<RcFnBrand, _, _, _, _, _>(
		(|e: &i32| e.to_string(), |s: &i32| s.to_string()),
		&x,
	);
	assert_eq!(y, "5");
}

// -- m_do! inferred mode --

#[test]
fn m_do_inferred() {
	use fp_macros::m_do;

	let result = m_do!({
		x <- Some(5);
		y <- Some(x + 1);
		let z = x * y;
		Some(z)
	});
	assert_eq!(result, Some(30));
}

// -- a_do! inferred mode --

#[test]
fn a_do_inferred() {
	use fp_macros::a_do;

	let result = a_do!({
		x <- Some(5);
		y <- Some(10);
		x + y
	});
	assert_eq!(result, Some(15));
}
