//! Tests for FallibleLender trait methods, DoubleEnded, peekable unsafe paths

mod common;
use ::lender::prelude::*;
use common::*;

// Comprehensive FallibleLender tests
// ============================================================================

#[test]
fn fallible_lender_next_chunk() {
    use lender::FallibleLender;

    let mut fallible: lender::IntoFallible<_> = VecLender::new(vec![1, 2, 3, 4, 5]).into_fallible();
    let mut chunk = fallible.next_chunk(2);
    assert_eq!(chunk.next(), Ok(Some(&1)));
    assert_eq!(chunk.next(), Ok(Some(&2)));
    assert_eq!(chunk.next(), Ok(None));
}

#[test]
fn fallible_lender_count() {
    use lender::FallibleLender;

    let fallible: lender::IntoFallible<_> = VecLender::new(vec![1, 2, 3, 4, 5]).into_fallible();
    assert_eq!(fallible.count(), Ok(5));
}

#[test]
fn fallible_lender_last() {
    use lender::FallibleLender;

    let mut fallible: lender::IntoFallible<_> = VecLender::new(vec![1, 2, 3]).into_fallible();
    assert_eq!(fallible.last(), Ok(Some(&3)));
}

#[test]
fn fallible_lender_advance_by() {
    use lender::FallibleLender;

    let mut fallible: lender::IntoFallible<_> = VecLender::new(vec![1, 2, 3, 4, 5]).into_fallible();
    assert_eq!(fallible.advance_by(2), Ok(Ok(())));
    assert_eq!(fallible.next(), Ok(Some(&3)));
}

#[test]
fn fallible_lender_nth() {
    use lender::FallibleLender;

    let mut fallible: lender::IntoFallible<_> = VecLender::new(vec![1, 2, 3, 4, 5]).into_fallible();
    assert_eq!(fallible.nth(2), Ok(Some(&3)));
}

#[test]
fn fallible_lender_step_by() {
    use lender::FallibleLender;

    let fallible: lender::IntoFallible<_> = VecLender::new(vec![1, 2, 3, 4, 5]).into_fallible();
    let mut stepped = fallible.step_by(2);
    assert_eq!(stepped.next(), Ok(Some(&1)));
    assert_eq!(stepped.next(), Ok(Some(&3)));
    assert_eq!(stepped.next(), Ok(Some(&5)));
    assert_eq!(stepped.next(), Ok(None));
}

#[test]
fn fallible_lender_chain() {
    use lender::FallibleLender;

    let fallible1: lender::IntoFallible<_> = VecLender::new(vec![1, 2]).into_fallible();
    let fallible2: lender::IntoFallible<_> = VecLender::new(vec![3, 4]).into_fallible();
    let mut chained = fallible1.chain(fallible2);
    assert_eq!(chained.next(), Ok(Some(&1)));
    assert_eq!(chained.next(), Ok(Some(&2)));
    assert_eq!(chained.next(), Ok(Some(&3)));
    assert_eq!(chained.next(), Ok(Some(&4)));
    assert_eq!(chained.next(), Ok(None));
}

#[test]
fn fallible_lender_zip() {
    use lender::FallibleLender;

    let fallible1: lender::IntoFallible<_> = VecLender::new(vec![1, 2, 3]).into_fallible();
    let fallible2: lender::IntoFallible<_> = VecLender::new(vec![4, 5, 6]).into_fallible();
    let mut zipped = fallible1.zip(fallible2);
    assert_eq!(zipped.next(), Ok(Some((&1, &4))));
    assert_eq!(zipped.next(), Ok(Some((&2, &5))));
    assert_eq!(zipped.next(), Ok(Some((&3, &6))));
    assert_eq!(zipped.next(), Ok(None));
}

#[test]
fn fallible_lender_map() {
    use lender::FallibleLender;

    let fallible: lender::IntoFallible<_> = VecLender::new(vec![1, 2, 3]).into_fallible();
    let mut mapped = fallible.map(covar_mut!(for<'lend> |x: &'lend i32| -> Result<
        i32,
        core::convert::Infallible,
    > { Ok(*x * 2) }));
    assert_eq!(mapped.next(), Ok(Some(2)));
    assert_eq!(mapped.next(), Ok(Some(4)));
    assert_eq!(mapped.next(), Ok(Some(6)));
    assert_eq!(mapped.next(), Ok(None));
}

#[test]
fn fallible_lender_filter() {
    use lender::FallibleLender;

    let fallible: lender::IntoFallible<_> = VecLender::new(vec![1, 2, 3, 4, 5, 6]).into_fallible();
    let mut filtered = fallible.filter(|&&x| Ok(x % 2 == 0));
    assert_eq!(filtered.next(), Ok(Some(&2)));
    assert_eq!(filtered.next(), Ok(Some(&4)));
    assert_eq!(filtered.next(), Ok(Some(&6)));
    assert_eq!(filtered.next(), Ok(None));
}

#[test]
fn fallible_lender_enumerate() {
    use lender::FallibleLender;

    let fallible: lender::IntoFallible<_> = VecLender::new(vec![10, 20, 30]).into_fallible();
    let mut enumerated = fallible.enumerate();
    assert_eq!(enumerated.next(), Ok(Some((0, &10))));
    assert_eq!(enumerated.next(), Ok(Some((1, &20))));
    assert_eq!(enumerated.next(), Ok(Some((2, &30))));
    assert_eq!(enumerated.next(), Ok(None));
}

#[test]
fn fallible_lender_skip() {
    use lender::FallibleLender;

    let fallible: lender::IntoFallible<_> = VecLender::new(vec![1, 2, 3, 4, 5]).into_fallible();
    let mut skipped = fallible.skip(2);
    assert_eq!(skipped.next(), Ok(Some(&3)));
    assert_eq!(skipped.next(), Ok(Some(&4)));
    assert_eq!(skipped.next(), Ok(Some(&5)));
    assert_eq!(skipped.next(), Ok(None));
}

#[test]
fn fallible_lender_take() {
    use lender::FallibleLender;

    let fallible: lender::IntoFallible<_> = VecLender::new(vec![1, 2, 3, 4, 5]).into_fallible();
    let mut taken = fallible.take(3);
    assert_eq!(taken.next(), Ok(Some(&1)));
    assert_eq!(taken.next(), Ok(Some(&2)));
    assert_eq!(taken.next(), Ok(Some(&3)));
    assert_eq!(taken.next(), Ok(None));
}

#[test]
fn fallible_lender_skip_while() {
    use lender::FallibleLender;

    let fallible: lender::IntoFallible<_> = VecLender::new(vec![1, 2, 3, 4, 5]).into_fallible();
    let mut skipped = fallible.skip_while(|&&x| Ok(x < 3));
    assert_eq!(skipped.next(), Ok(Some(&3)));
    assert_eq!(skipped.next(), Ok(Some(&4)));
    assert_eq!(skipped.next(), Ok(Some(&5)));
    assert_eq!(skipped.next(), Ok(None));
}

#[test]
fn fallible_lender_take_while() {
    use lender::FallibleLender;

    let fallible: lender::IntoFallible<_> = VecLender::new(vec![1, 2, 3, 4, 5]).into_fallible();
    let mut taken = fallible.take_while(|&&x| Ok(x < 4));
    assert_eq!(taken.next(), Ok(Some(&1)));
    assert_eq!(taken.next(), Ok(Some(&2)));
    assert_eq!(taken.next(), Ok(Some(&3)));
    assert_eq!(taken.next(), Ok(None));
}

#[test]
fn fallible_lender_inspect() {
    use lender::FallibleLender;

    let mut inspected = Vec::new();
    let fallible: lender::IntoFallible<_> = VecLender::new(vec![1, 2, 3]).into_fallible();
    let mut lender = fallible.inspect(|&&x| {
        inspected.push(x);
        Ok(())
    });
    assert_eq!(lender.next(), Ok(Some(&1)));
    assert_eq!(lender.next(), Ok(Some(&2)));
    assert_eq!(lender.next(), Ok(Some(&3)));
    assert_eq!(inspected, vec![1, 2, 3]);
}

#[test]
fn fallible_lender_fuse() {
    use lender::FallibleLender;

    let fallible: lender::IntoFallible<_> = VecLender::new(vec![1, 2]).into_fallible();
    let mut fused = fallible.fuse();
    assert_eq!(fused.next(), Ok(Some(&1)));
    assert_eq!(fused.next(), Ok(Some(&2)));
    assert_eq!(fused.next(), Ok(None));
    assert_eq!(fused.next(), Ok(None)); // Fused stays None
}

#[test]
fn fallible_lender_fold() {
    use lender::FallibleLender;

    let fallible: lender::IntoFallible<_> = VecLender::new(vec![1, 2, 3, 4, 5]).into_fallible();
    let sum = fallible.fold(0, |acc, x| Ok(acc + *x));
    assert_eq!(sum, Ok(15));
}

#[test]
fn fallible_lender_for_each() {
    use lender::FallibleLender;

    let mut collected = Vec::new();
    let fallible: lender::IntoFallible<_> = VecLender::new(vec![1, 2, 3]).into_fallible();
    let result = fallible.for_each(|x| {
        collected.push(*x);
        Ok(())
    });
    assert_eq!(result, Ok(()));
    assert_eq!(collected, vec![1, 2, 3]);
}

#[test]
fn fallible_lender_all() {
    use lender::FallibleLender;

    let mut fallible: lender::IntoFallible<_> = VecLender::new(vec![2, 4, 6]).into_fallible();
    assert_eq!(fallible.all(|x| Ok(*x % 2 == 0)), Ok(true));

    let mut fallible2: lender::IntoFallible<_> = VecLender::new(vec![2, 3, 6]).into_fallible();
    assert_eq!(fallible2.all(|x| Ok(*x % 2 == 0)), Ok(false));
}

#[test]
fn fallible_lender_any() {
    use lender::FallibleLender;

    let mut fallible: lender::IntoFallible<_> = VecLender::new(vec![1, 3, 5]).into_fallible();
    assert_eq!(fallible.any(|x| Ok(*x % 2 == 0)), Ok(false));

    let mut fallible2: lender::IntoFallible<_> = VecLender::new(vec![1, 2, 3]).into_fallible();
    assert_eq!(fallible2.any(|x| Ok(*x % 2 == 0)), Ok(true));
}

#[test]
fn fallible_lender_find() {
    use lender::FallibleLender;

    let mut fallible: lender::IntoFallible<_> = VecLender::new(vec![1, 2, 3, 4, 5]).into_fallible();
    assert_eq!(fallible.find(|&&x| Ok(x > 3)), Ok(Some(&4)));
}

#[test]
fn fallible_lender_position() {
    use lender::FallibleLender;

    let mut fallible: lender::IntoFallible<_> = VecLender::new(vec![1, 2, 3, 4, 5]).into_fallible();
    assert_eq!(fallible.position(|x| Ok(*x == 3)), Ok(Some(2)));
}

#[test]
fn fallible_lender_rposition() {
    use lender::FallibleLender;

    let mut fallible: lender::IntoFallible<_> = VecLender::new(vec![1, 2, 3, 4, 5]).into_fallible();
    assert_eq!(fallible.rposition(|x| Ok(*x == 3)), Ok(Some(2)));
}

#[test]
fn lender_convert() {
    use lender::FallibleLender;

    let data = vec![Ok(1), Ok(2), Err("oops")];
    let mut lender = data.into_iter().into_lender().convert::<&str>();
    assert_eq!(lender.next(), Ok(Some(1)));
    assert_eq!(lender.next(), Ok(Some(2)));
    assert!(lender.next().is_err());
}

#[test]
fn fallible_lender_chunky() {
    use lender::FallibleLender;

    let fallible: lender::IntoFallible<_> = VecLender::new(vec![1, 2, 3, 4, 5, 6]).into_fallible();
    let mut chunky = fallible.chunky(2);

    let mut chunk1 = chunky.next().unwrap().unwrap();
    assert_eq!(chunk1.next(), Ok(Some(&1)));
    assert_eq!(chunk1.next(), Ok(Some(&2)));
    assert_eq!(chunk1.next(), Ok(None));
}

#[test]
fn fallible_lender_rev() {
    use lender::FallibleLender;

    let fallible: lender::IntoFallible<_> = VecLender::new(vec![1, 2, 3]).into_fallible();
    let mut rev = fallible.rev();
    assert_eq!(rev.next(), Ok(Some(&3)));
    assert_eq!(rev.next(), Ok(Some(&2)));
    assert_eq!(rev.next(), Ok(Some(&1)));
    assert_eq!(rev.next(), Ok(None));
}

// ============================================================================
// DoubleEndedFallibleLender tests
// ============================================================================

#[test]
fn double_ended_fallible_advance_back_by() {
    use lender::DoubleEndedFallibleLender;

    let mut fallible: lender::IntoFallible<_> = VecLender::new(vec![1, 2, 3, 4, 5]).into_fallible();
    assert_eq!(fallible.advance_back_by(2), Ok(Ok(())));
    assert_eq!(fallible.next_back(), Ok(Some(&3)));
}

#[test]
fn double_ended_fallible_nth_back() {
    use lender::DoubleEndedFallibleLender;

    let mut fallible: lender::IntoFallible<_> = VecLender::new(vec![1, 2, 3, 4, 5]).into_fallible();
    assert_eq!(fallible.nth_back(2), Ok(Some(&3)));
}

#[test]
fn double_ended_fallible_try_rfold() {
    use lender::DoubleEndedFallibleLender;

    let mut fallible: lender::IntoFallible<_> = VecLender::new(vec![1, 2, 3]).into_fallible();
    let result: Result<Option<i32>, core::convert::Infallible> =
        fallible.try_rfold(0, |acc, x| Ok(Some(acc + *x)));
    assert_eq!(result, Ok(Some(6)));
}

#[test]
fn double_ended_fallible_rfold() {
    use lender::DoubleEndedFallibleLender;

    let fallible: lender::IntoFallible<_> = VecLender::new(vec![1, 2, 3]).into_fallible();
    let values: Result<Vec<i32>, core::convert::Infallible> =
        fallible.rfold(Vec::new(), |mut acc, x| {
            acc.push(*x);
            Ok(acc)
        });
    assert_eq!(values, Ok(vec![3, 2, 1]));
}

// ============================================================================
// Fallible peekable unsafe paths
// ============================================================================

#[test]
fn fallible_peekable_nth_zero_with_peeked() {
    use lender::FallibleLender;

    let fallible: lender::IntoFallible<_> = VecLender::new(vec![1, 2, 3]).into_fallible();
    let mut peekable = fallible.peekable();
    // Peek to store a value
    assert_eq!(peekable.peek(), Ok(Some(&&1)));
    // nth(0) should return the peeked value through the unsafe transmute path
    assert_eq!(peekable.nth(0), Ok(Some(&1)));
    assert_eq!(peekable.next(), Ok(Some(&2)));
}

// FalliblePeekable::last with peeked value
#[test]
fn fallible_peekable_last_with_peeked_only() {
    use lender::FallibleLender;

    let fallible: lender::IntoFallible<_> = VecLender::new(vec![1]).into_fallible();
    let mut peekable = fallible.peekable();
    // Peek the only value
    assert_eq!(peekable.peek(), Ok(Some(&&1)));
    // last() should return the peeked value through the unsafe transmute path
    assert_eq!(peekable.last(), Ok(Some(&1)));
}

// FalliblePeekable::next_back with peeked value when underlying lender is empty
#[test]
fn fallible_peekable_next_back_with_peeked_exhausted() {
    use lender::DoubleEndedFallibleLender;

    let fallible: lender::IntoFallible<_> = VecLender::new(vec![1]).into_fallible();
    let mut peekable = fallible.peekable();
    // Peek the only value
    let _ = peekable.peek();
    // next_back should return the peeked value through the unsafe transmute path
    assert_eq!(peekable.next_back(), Ok(Some(&1)));
}

// FalliblePeekable::peek_mut (covers unsafe transmute in peek_mut)
#[test]
fn fallible_peekable_peek_mut() {
    use lender::FallibleLender;

    let fallible: lender::IntoFallible<_> = VecLender::new(vec![1, 2, 3]).into_fallible();
    let mut peekable = fallible.peekable();
    // peek_mut to store a value and get mutable reference
    let peeked = peekable.peek_mut().unwrap();
    assert_eq!(peeked, Some(&mut &1));
}

// FalliblePeekable::next_if (covers unsafe transmute in next_if)
#[test]
fn fallible_peekable_next_if_match() {
    use lender::FallibleLender;

    let fallible: lender::IntoFallible<_> = VecLender::new(vec![1, 2, 3]).into_fallible();
    let mut peekable = fallible.peekable();
    // next_if should return Some when predicate matches
    assert_eq!(peekable.next_if(|&&x| x == 1), Ok(Some(&1)));
    // Should have advanced
    assert_eq!(peekable.next(), Ok(Some(&2)));
}

#[test]
fn fallible_peekable_next_if_no_match() {
    use lender::FallibleLender;

    let fallible: lender::IntoFallible<_> = VecLender::new(vec![1, 2, 3]).into_fallible();
    let mut peekable = fallible.peekable();
    // next_if should return None when predicate doesn't match (and store in peeked)
    assert_eq!(peekable.next_if(|&&x| x == 5), Ok(None));
    // Value should still be available
    assert_eq!(peekable.next(), Ok(Some(&1)));
}

#[test]
fn fallible_peekable_rfold_with_peeked() {
    use lender::DoubleEndedFallibleLender;
    use lender::FallibleLender;

    let fallible: lender::IntoFallible<_> = VecLender::new(vec![1, 2, 3]).into_fallible();
    let mut peekable = fallible.peekable();
    assert_eq!(peekable.peek(), Ok(Some(&&1)));
    // rfold processes back-to-front: 3, 2, then peeked 1
    let result = peekable.rfold(Vec::new(), |mut acc, &x| {
        acc.push(x);
        Ok(acc)
    });
    assert_eq!(result, Ok(vec![3, 2, 1]));
}

#[test]
fn fallible_peekable_try_rfold_with_peeked_complete() {
    use lender::DoubleEndedFallibleLender;
    use lender::FallibleLender;

    let fallible: lender::IntoFallible<_> = VecLender::new(vec![1, 2, 3]).into_fallible();
    let mut peekable = fallible.peekable();
    assert_eq!(peekable.peek(), Ok(Some(&&1)));
    // try_rfold processes back-to-front: 3, 2, then peeked 1
    let result: Result<Option<Vec<i32>>, core::convert::Infallible> =
        peekable.try_rfold(Vec::new(), |mut acc, &x| {
            acc.push(x);
            Ok(Some(acc))
        });
    assert_eq!(result, Ok(Some(vec![3, 2, 1])));
}

// Covers the ControlFlow::Break path in fallible Peekable::try_rfold
// where the peeked value is stored back.
#[test]
fn fallible_peekable_try_rfold_with_peeked_break() {
    use lender::DoubleEndedFallibleLender;
    use lender::FallibleLender;

    let fallible: lender::IntoFallible<_> = VecLender::new(vec![1, 2, 3]).into_fallible();
    let mut peekable = fallible.peekable();
    assert_eq!(peekable.peek(), Ok(Some(&&1)));
    // Inner lender has [2, 3]. try_rfold processes back-to-front:
    // 3 (continue, acc=3), then 2 (break via None).
    let result: Result<Option<i32>, core::convert::Infallible> = peekable.try_rfold(
        0,
        |acc, &x| {
            if x == 2 { Ok(None) } else { Ok(Some(acc + x)) }
        },
    );
    assert_eq!(result, Ok(None));
    // The peeked value should have been stored back
    assert_eq!(peekable.next(), Ok(Some(&1)));
    // Inner lender was fully consumed by try_rfold
    assert_eq!(peekable.next(), Ok(None));
}

// ============================================================================
// Iter fallible iterator
// ============================================================================

// Iter adapter FallibleIterator next (covers unsafe transmute in next)
// Note: .iter() requires the Lend type to satisfy complex higher-ranked trait bounds.
// With VecFallibleLender yielding &'lend i32, there are lifetime issues that prevent
// it from working with .iter(). We test with owned values via into_iter().into_lender().into_fallible()
// which yields i32 (Copy type with no lifetime issues).
#[test]
fn iter_fallible_iterator_next() {
    use fallible_iterator::FallibleIterator;

    let fallible = vec![1, 2, 3].into_iter().into_lender().into_fallible();
    let mut iter = fallible.iter();
    assert_eq!(FallibleIterator::next(&mut iter), Ok(Some(1)));
    assert_eq!(FallibleIterator::next(&mut iter), Ok(Some(2)));
    assert_eq!(FallibleIterator::next(&mut iter), Ok(Some(3)));
    assert_eq!(FallibleIterator::next(&mut iter), Ok(None));
}

// Iter adapter DoubleEndedFallibleIterator next_back (covers unsafe transmute in next_back)
#[test]
fn iter_double_ended_fallible_iterator_next_back() {
    use fallible_iterator::DoubleEndedFallibleIterator;

    let fallible = vec![1, 2, 3].into_iter().into_lender().into_fallible();
    let mut iter = fallible.iter();
    assert_eq!(
        DoubleEndedFallibleIterator::next_back(&mut iter),
        Ok(Some(3))
    );
    assert_eq!(
        DoubleEndedFallibleIterator::next_back(&mut iter),
        Ok(Some(2))
    );
}

// ============================================================================
// Cycle fallible coverage
// ============================================================================

// Cycle fallible next (covers unsafe reborrow in next)
#[test]
fn cycle_fallible_next_coverage() {
    use lender::FallibleLender;

    let fallible: lender::IntoFallible<_> = VecLender::new(vec![1, 2]).into_fallible();
    let mut cycle = fallible.cycle();
    // Call next() multiple times to exercise the unsafe reborrow and cycling
    assert_eq!(cycle.next(), Ok(Some(&1)));
    assert_eq!(cycle.next(), Ok(Some(&2)));
    // This should cycle back to the beginning
    assert_eq!(cycle.next(), Ok(Some(&1)));
    assert_eq!(cycle.next(), Ok(Some(&2)));
    assert_eq!(cycle.next(), Ok(Some(&1)));
}

// ============================================================================
// Fallible nth past end
// ============================================================================

#[test]
fn fallible_lender_nth_past_end() {
    use core::num::NonZeroUsize;
    use lender::{FallibleLend, FallibleLender, FallibleLending, FusedFallibleLender};

    /// A fallible lender that always has elements but whose advance_by
    /// always reports failure without consuming anything.
    struct StubbyAdvance(i32);

    impl<'lend> FallibleLending<'lend> for StubbyAdvance {
        type Lend = i32;
    }

    impl FallibleLender for StubbyAdvance {
        type Error = ();
        lender::check_covariance_fallible!();

        fn next(&mut self) -> Result<Option<FallibleLend<'_, Self>>, Self::Error> {
            self.0 += 1;
            Ok(Some(self.0))
        }

        fn advance_by(&mut self, n: usize) -> Result<Result<(), NonZeroUsize>, Self::Error> {
            // Report complete failure: didn't advance at all.
            Ok(NonZeroUsize::new(n).map_or(Ok(()), Err))
        }
    }

    impl FusedFallibleLender for StubbyAdvance {}

    let mut lender = StubbyAdvance(0);
    // advance_by(3) will return Ok(Err(3)) — full failure.
    // nth(3) must therefore return Ok(None), not call next().
    assert_eq!(lender.nth(3), Ok(None));
}

#[test]
fn double_ended_fallible_nth_back_past_end() {
    use core::num::NonZeroUsize;
    use lender::{
        DoubleEndedFallibleLender, FallibleLend, FallibleLender, FallibleLending,
        FusedFallibleLender,
    };

    /// A fallible lender whose advance_back_by always reports failure.
    struct StubbyAdvanceBack(i32);

    impl<'lend> FallibleLending<'lend> for StubbyAdvanceBack {
        type Lend = i32;
    }

    impl FallibleLender for StubbyAdvanceBack {
        type Error = ();
        lender::check_covariance_fallible!();

        fn next(&mut self) -> Result<Option<FallibleLend<'_, Self>>, Self::Error> {
            self.0 += 1;
            Ok(Some(self.0))
        }
    }

    impl DoubleEndedFallibleLender for StubbyAdvanceBack {
        fn next_back(&mut self) -> Result<Option<FallibleLend<'_, Self>>, Self::Error> {
            self.0 += 1;
            Ok(Some(self.0))
        }

        fn advance_back_by(&mut self, n: usize) -> Result<Result<(), NonZeroUsize>, Self::Error> {
            // Report complete failure: didn't advance at all.
            Ok(NonZeroUsize::new(n).map_or(Ok(()), Err))
        }
    }

    impl FusedFallibleLender for StubbyAdvanceBack {}

    let mut lender = StubbyAdvanceBack(0);
    // advance_back_by(3) will return Ok(Err(3)) — full failure.
    // nth_back(3) must therefore return Ok(None), not call next_back().
    assert_eq!(lender.nth_back(3), Ok(None));
}

// ============================================================================
// FallibleLender comparison method tests
// ============================================================================

#[test]
fn fallible_lender_cmp() {
    use core::cmp::Ordering;
    use lender::FallibleLender;

    let a = [1, 2, 3].into_iter().into_lender().into_fallible();
    let b = [1, 2, 3].into_iter().into_lender().into_fallible();
    assert_eq!(a.cmp(b), Ok(Ordering::Equal));

    let a = [1, 2, 3].into_iter().into_lender().into_fallible();
    let b = [1, 2, 4].into_iter().into_lender().into_fallible();
    assert_eq!(a.cmp(b), Ok(Ordering::Less));

    let a = [1, 2, 4].into_iter().into_lender().into_fallible();
    let b = [1, 2, 3].into_iter().into_lender().into_fallible();
    assert_eq!(a.cmp(b), Ok(Ordering::Greater));

    let a = [1, 2].into_iter().into_lender().into_fallible();
    let b = [1, 2, 3].into_iter().into_lender().into_fallible();
    assert_eq!(a.cmp(b), Ok(Ordering::Less));
}

#[test]
fn fallible_lender_partial_cmp() {
    use core::cmp::Ordering;
    use lender::FallibleLender;

    let a = [1, 2, 3].into_iter().into_lender().into_fallible();
    let b = [1, 2, 3].into_iter().into_lender().into_fallible();
    assert_eq!(
        a.partial_cmp::<lender::IntoFallible<_>>(b),
        Ok(Some(Ordering::Equal))
    );

    let a = [1, 2].into_iter().into_lender().into_fallible();
    let b = [1, 3].into_iter().into_lender().into_fallible();
    assert_eq!(
        a.partial_cmp::<lender::IntoFallible<_>>(b),
        Ok(Some(Ordering::Less))
    );
}

#[test]
fn fallible_lender_eq() {
    use lender::FallibleLender;

    let a = [1, 2, 3].into_iter().into_lender().into_fallible();
    let b = [1, 2, 3].into_iter().into_lender().into_fallible();
    assert_eq!(a.eq::<lender::IntoFallible<_>>(b), Ok(true));

    let a = [1, 2, 3].into_iter().into_lender().into_fallible();
    let b = [1, 2, 4].into_iter().into_lender().into_fallible();
    assert_eq!(a.eq::<lender::IntoFallible<_>>(b), Ok(false));

    let a = [1, 2].into_iter().into_lender().into_fallible();
    let b = [1, 2, 3].into_iter().into_lender().into_fallible();
    assert_eq!(a.eq::<lender::IntoFallible<_>>(b), Ok(false));
}

#[test]
fn fallible_lender_ne() {
    use lender::FallibleLender;

    let a = [1, 2, 3].into_iter().into_lender().into_fallible();
    let b = [1, 2, 4].into_iter().into_lender().into_fallible();
    assert_eq!(a.ne::<lender::IntoFallible<_>>(b), Ok(true));

    let a = [1, 2, 3].into_iter().into_lender().into_fallible();
    let b = [1, 2, 3].into_iter().into_lender().into_fallible();
    assert_eq!(a.ne::<lender::IntoFallible<_>>(b), Ok(false));
}

#[test]
fn fallible_lender_lt() {
    use lender::FallibleLender;

    let a = [1, 2].into_iter().into_lender().into_fallible();
    let b = [1, 3].into_iter().into_lender().into_fallible();
    assert_eq!(a.lt::<lender::IntoFallible<_>>(b), Ok(true));

    let a = [1, 3].into_iter().into_lender().into_fallible();
    let b = [1, 2].into_iter().into_lender().into_fallible();
    assert_eq!(a.lt::<lender::IntoFallible<_>>(b), Ok(false));

    let a = [1, 2].into_iter().into_lender().into_fallible();
    let b = [1, 2].into_iter().into_lender().into_fallible();
    assert_eq!(a.lt::<lender::IntoFallible<_>>(b), Ok(false));
}

#[test]
fn fallible_lender_le() {
    use lender::FallibleLender;

    let a = [1, 2].into_iter().into_lender().into_fallible();
    let b = [1, 3].into_iter().into_lender().into_fallible();
    assert_eq!(a.le::<lender::IntoFallible<_>>(b), Ok(true));

    let a = [1, 2].into_iter().into_lender().into_fallible();
    let b = [1, 2].into_iter().into_lender().into_fallible();
    assert_eq!(a.le::<lender::IntoFallible<_>>(b), Ok(true));

    let a = [1, 3].into_iter().into_lender().into_fallible();
    let b = [1, 2].into_iter().into_lender().into_fallible();
    assert_eq!(a.le::<lender::IntoFallible<_>>(b), Ok(false));
}

#[test]
fn fallible_lender_gt() {
    use lender::FallibleLender;

    let a = [1, 3].into_iter().into_lender().into_fallible();
    let b = [1, 2].into_iter().into_lender().into_fallible();
    assert_eq!(a.gt::<lender::IntoFallible<_>>(b), Ok(true));

    let a = [1, 2].into_iter().into_lender().into_fallible();
    let b = [1, 3].into_iter().into_lender().into_fallible();
    assert_eq!(a.gt::<lender::IntoFallible<_>>(b), Ok(false));
}

#[test]
fn fallible_lender_ge() {
    use lender::FallibleLender;

    let a = [1, 3].into_iter().into_lender().into_fallible();
    let b = [1, 2].into_iter().into_lender().into_fallible();
    assert_eq!(a.ge::<lender::IntoFallible<_>>(b), Ok(true));

    let a = [1, 2].into_iter().into_lender().into_fallible();
    let b = [1, 2].into_iter().into_lender().into_fallible();
    assert_eq!(a.ge::<lender::IntoFallible<_>>(b), Ok(true));

    let a = [1, 2].into_iter().into_lender().into_fallible();
    let b = [1, 3].into_iter().into_lender().into_fallible();
    assert_eq!(a.ge::<lender::IntoFallible<_>>(b), Ok(false));
}

// ============================================================================
// FallibleLender reduce, max, min, is_sorted tests
// ============================================================================

#[test]
fn fallible_lender_reduce() {
    use lender::FallibleLender;

    let result = vec![1, 2, 3, 4]
        .into_iter()
        .into_lender()
        .into_fallible()
        .reduce(|acc, x| Ok(acc + x));
    assert_eq!(result, Ok(Some(10)));

    let result = Vec::<i32>::new()
        .into_iter()
        .into_lender()
        .into_fallible()
        .reduce(|acc, x| Ok(acc + x));
    assert_eq!(result, Ok(None));
}

#[test]
fn fallible_lender_max() {
    use lender::FallibleLender;

    assert_eq!(
        vec![1, 5, 3, 2, 4]
            .into_iter()
            .into_lender()
            .into_fallible()
            .max(),
        Ok(Some(5))
    );
    assert_eq!(
        Vec::<i32>::new()
            .into_iter()
            .into_lender()
            .into_fallible()
            .max(),
        Ok(None)
    );
}

#[test]
fn fallible_lender_min() {
    use lender::FallibleLender;

    assert_eq!(
        vec![3, 1, 5, 2, 4]
            .into_iter()
            .into_lender()
            .into_fallible()
            .min(),
        Ok(Some(1))
    );
}

#[test]
fn fallible_lender_max_by() {
    use lender::FallibleLender;

    assert_eq!(
        vec![1, 5, 3]
            .into_iter()
            .into_lender()
            .into_fallible()
            .max_by(|a, b| Ok(a.cmp(b))),
        Ok(Some(5))
    );
}

#[test]
fn fallible_lender_min_by() {
    use lender::FallibleLender;

    assert_eq!(
        vec![3, 1, 5]
            .into_iter()
            .into_lender()
            .into_fallible()
            .min_by(|a, b| Ok(a.cmp(b))),
        Ok(Some(1))
    );
}

#[test]
fn fallible_lender_max_by_key() {
    use lender::FallibleLender;

    assert_eq!(
        vec![-3, 0, 1, 5, -2]
            .into_iter()
            .into_lender()
            .into_fallible()
            .max_by_key(|x: &i32| Ok(x.abs())),
        Ok(Some(5))
    );
}

#[test]
fn fallible_lender_min_by_key() {
    use lender::FallibleLender;

    assert_eq!(
        vec![-3, 0, 1, 5, -2]
            .into_iter()
            .into_lender()
            .into_fallible()
            .min_by_key(|x: &i32| Ok(x.abs())),
        Ok(Some(0))
    );
}

#[test]
fn fallible_lender_is_sorted() {
    use lender::FallibleLender;

    assert_eq!(
        vec![1, 2, 3, 4]
            .into_iter()
            .into_lender()
            .into_fallible()
            .is_sorted(),
        Ok(true)
    );
    assert_eq!(
        vec![1, 3, 2]
            .into_iter()
            .into_lender()
            .into_fallible()
            .is_sorted(),
        Ok(false)
    );
}

#[test]
fn fallible_lender_is_sorted_by() {
    use lender::FallibleLender;

    assert_eq!(
        vec![4, 3, 2, 1]
            .into_iter()
            .into_lender()
            .into_fallible()
            .is_sorted_by(|a, b| Ok(Some(b.cmp(a)))),
        Ok(true)
    );
}

#[test]
fn fallible_lender_is_sorted_by_key() {
    use lender::FallibleLender;

    assert_eq!(
        vec![0, -1, 2, -3]
            .into_iter()
            .into_lender()
            .into_fallible()
            .is_sorted_by_key(|x: i32| Ok(x.abs())),
        Ok(true)
    );
}

// ============================================================================
// FallibleLender by_ref test
// ============================================================================

#[test]
fn fallible_lender_by_ref() {
    use lender::FallibleLender;

    let mut fallible: lender::IntoFallible<_> = VecLender::new(vec![1, 2, 3, 4, 5]).into_fallible();
    // Take 2 via by_ref
    {
        let by_ref = fallible.by_ref();
        let mut taken = by_ref.take(2);
        assert_eq!(taken.next(), Ok(Some(&1)));
        assert_eq!(taken.next(), Ok(Some(&2)));
        assert_eq!(taken.next(), Ok(None));
    }
    // Remaining elements still available
    assert_eq!(fallible.next(), Ok(Some(&3)));
    assert_eq!(fallible.next(), Ok(Some(&4)));
    assert_eq!(fallible.next(), Ok(Some(&5)));
    assert_eq!(fallible.next(), Ok(None));
}

// ============================================================================
// FallibleLender find_map test
// ============================================================================

#[test]
fn fallible_lender_find_map() {
    use lender::FallibleLender;

    let mut fallible = VecFallibleLender::new(vec![1, 2, 3, 4, 5]);
    let result = fallible.find_map(|x: &i32| Ok(if *x > 3 { Some(*x * 10) } else { None }));
    assert_eq!(result, Ok(Some(40)));
}

// ============================================================================
// FallibleLender partition tests
// ============================================================================
// Note: partition() requires E: ExtendLender<NonFallibleAdapter<'this, Self>>,
// which uses the crate-private NonFallibleAdapter type. Testing partition()
// directly requires implementing ExtendLender for that private type, which
// cannot be done from outside the crate. The is_partitioned() method (tested
// in test_fallible_coverage.rs) exercises the partition logic indirectly.

// ============================================================================
// FallibleLender sum tests
// ============================================================================

#[test]
fn fallible_lender_sum() {
    use lender::{FallibleLender, SumFallibleLender};

    struct I32Sum(i32);

    impl SumFallibleLender<VecFallibleLender> for I32Sum {
        fn sum_lender(lender: VecFallibleLender) -> Result<Self, core::convert::Infallible> {
            Ok(I32Sum(lender.fold(0, |acc, x| Ok(acc + *x))?))
        }
    }

    let sum: I32Sum = VecFallibleLender::new(vec![1, 2, 3, 4]).sum().unwrap();
    assert_eq!(sum.0, 10);

    let sum_empty: I32Sum = VecFallibleLender::new(vec![]).sum().unwrap();
    assert_eq!(sum_empty.0, 0);
}

// ============================================================================
// FallibleLender product tests
// ============================================================================

#[test]
fn fallible_lender_product() {
    use lender::{FallibleLender, ProductFallibleLender};

    struct I32Product(i32);

    impl ProductFallibleLender<VecFallibleLender> for I32Product {
        fn product_lender(lender: VecFallibleLender) -> Result<Self, core::convert::Infallible> {
            Ok(I32Product(lender.fold(1, |acc, x| Ok(acc * *x))?))
        }
    }

    let product: I32Product = VecFallibleLender::new(vec![1, 2, 3, 4]).product().unwrap();
    assert_eq!(product.0, 24);

    let product_empty: I32Product = VecFallibleLender::new(vec![]).product().unwrap();
    assert_eq!(product_empty.0, 1);
}

// ============================================================================
// FallibleLender unzip tests
// ============================================================================

#[test]
fn fallible_lender_unzip() {
    use fallible_iterator::FallibleIterator;
    use lender::FallibleLender;

    // Create a fallible lender over tuples via owned() which delegates to Iterator::unzip
    let (a, b): (Vec<i32>, Vec<i32>) = vec![(1, 4), (2, 5), (3, 6)]
        .into_iter()
        .into_lender()
        .into_fallible()
        .owned()
        .collect::<Vec<_>>()
        .unwrap()
        .into_iter()
        .unzip();
    assert_eq!(a, vec![1, 2, 3]);
    assert_eq!(b, vec![4, 5, 6]);
}
