use core::ops::DerefMut;
use core::option::*;

fn op1() -> Result<isize, &'static str> {
    Ok(666)
}
fn op2() -> Result<isize, &'static str> {
    Err("sadface")
}

#[test]
fn test_and() {
    assert_eq!(op1().and(Ok(667)).unwrap(), 667);
    assert_eq!(op1().and(Err::<i32, &'static str>("bad")).unwrap_err(), "bad");

    assert_eq!(op2().and(Ok(667)).unwrap_err(), "sadface");
    assert_eq!(op2().and(Err::<i32, &'static str>("bad")).unwrap_err(), "sadface");
}

#[test]
fn test_and_then() {
    assert_eq!(op1().and_then(|i| Ok::<isize, &'static str>(i + 1)).unwrap(), 667);
    assert_eq!(op1().and_then(|_| Err::<isize, &'static str>("bad")).unwrap_err(), "bad");

    assert_eq!(op2().and_then(|i| Ok::<isize, &'static str>(i + 1)).unwrap_err(), "sadface");
    assert_eq!(op2().and_then(|_| Err::<isize, &'static str>("bad")).unwrap_err(), "sadface");
}

#[test]
fn test_or() {
    assert_eq!(op1().or(Ok::<_, &'static str>(667)).unwrap(), 666);
    assert_eq!(op1().or(Err("bad")).unwrap(), 666);

    assert_eq!(op2().or(Ok::<_, &'static str>(667)).unwrap(), 667);
    assert_eq!(op2().or(Err("bad")).unwrap_err(), "bad");
}

#[test]
fn test_or_else() {
    assert_eq!(op1().or_else(|_| Ok::<isize, &'static str>(667)).unwrap(), 666);
    assert_eq!(op1().or_else(|e| Err::<isize, &'static str>(e)).unwrap(), 666);

    assert_eq!(op2().or_else(|_| Ok::<isize, &'static str>(667)).unwrap(), 667);
    assert_eq!(op2().or_else(|e| Err::<isize, &'static str>(e)).unwrap_err(), "sadface");
}

#[test]
fn test_impl_map() {
    assert!(Ok::<isize, isize>(1).map(|x| x + 1) == Ok(2));
    assert!(Err::<isize, isize>(1).map(|x| x + 1) == Err(1));
}

#[test]
fn test_impl_map_err() {
    assert!(Ok::<isize, isize>(1).map_err(|x| x + 1) == Ok(1));
    assert!(Err::<isize, isize>(1).map_err(|x| x + 1) == Err(2));
}

#[test]
fn test_collect() {
    let v: Result<Vec<isize>, ()> = (0..0).map(|_| Ok::<isize, ()>(0)).collect();
    assert!(v == Ok(vec![]));

    let v: Result<Vec<isize>, ()> = (0..3).map(|x| Ok::<isize, ()>(x)).collect();
    assert!(v == Ok(vec![0, 1, 2]));

    let v: Result<Vec<isize>, isize> = (0..3).map(|x| if x > 1 { Err(x) } else { Ok(x) }).collect();
    assert!(v == Err(2));

    // test that it does not take more elements than it needs
    let mut functions: [Box<dyn Fn() -> Result<(), isize>>; 3] =
        [Box::new(|| Ok(())), Box::new(|| Err(1)), Box::new(|| panic!())];

    let v: Result<Vec<()>, isize> = functions.iter_mut().map(|f| (*f)()).collect();
    assert!(v == Err(1));
}

#[test]
fn test_fmt_default() {
    let ok: Result<isize, &'static str> = Ok(100);
    let err: Result<isize, &'static str> = Err("Err");

    let s = format!("{ok:?}");
    assert_eq!(s, "Ok(100)");
    let s = format!("{err:?}");
    assert_eq!(s, "Err(\"Err\")");
}

#[test]
fn test_unwrap_or() {
    let ok: Result<isize, &'static str> = Ok(100);
    let ok_err: Result<isize, &'static str> = Err("Err");

    assert_eq!(ok.unwrap_or(50), 100);
    assert_eq!(ok_err.unwrap_or(50), 50);
}

#[test]
fn test_unwrap_or_else() {
    fn handler(msg: &'static str) -> isize {
        if msg == "I got this." { 50 } else { panic!("BadBad") }
    }

    let ok: Result<isize, &'static str> = Ok(100);
    let ok_err: Result<isize, &'static str> = Err("I got this.");

    assert_eq!(ok.unwrap_or_else(handler), 100);
    assert_eq!(ok_err.unwrap_or_else(handler), 50);
}

#[test]
#[should_panic]
pub fn test_unwrap_or_else_panic() {
    fn handler(msg: &'static str) -> isize {
        if msg == "I got this." { 50 } else { panic!("BadBad") }
    }

    let bad_err: Result<isize, &'static str> = Err("Unrecoverable mess.");
    let _: isize = bad_err.unwrap_or_else(handler);
}

#[test]
fn test_unwrap_unchecked() {
    let ok: Result<isize, &'static str> = Ok(100);
    assert_eq!(unsafe { ok.unwrap_unchecked() }, 100);
}

#[test]
fn test_unwrap_err_unchecked() {
    let ok_err: Result<isize, &'static str> = Err("Err");
    assert_eq!(unsafe { ok_err.unwrap_err_unchecked() }, "Err");
}

#[test]
pub fn test_expect_ok() {
    let ok: Result<isize, &'static str> = Ok(100);
    assert_eq!(ok.expect("Unexpected error"), 100);
}
#[test]
#[should_panic(expected = "Got expected error: \"All good\"")]
pub fn test_expect_err() {
    let err: Result<isize, &'static str> = Err("All good");
    err.expect("Got expected error");
}

#[test]
pub fn test_expect_err_err() {
    let ok: Result<&'static str, isize> = Err(100);
    assert_eq!(ok.expect_err("Unexpected ok"), 100);
}
#[test]
#[should_panic(expected = "Got expected ok: \"All good\"")]
pub fn test_expect_err_ok() {
    let err: Result<&'static str, isize> = Ok("All good");
    err.expect_err("Got expected ok");
}

#[test]
pub fn test_iter() {
    let ok: Result<isize, &'static str> = Ok(100);
    let mut it = ok.iter();
    assert_eq!(it.size_hint(), (1, Some(1)));
    assert_eq!(it.next(), Some(&100));
    assert_eq!(it.size_hint(), (0, Some(0)));
    assert!(it.next().is_none());
    assert_eq!((&ok).into_iter().next(), Some(&100));

    let err: Result<isize, &'static str> = Err("error");
    assert_eq!(err.iter().next(), None);
}

#[test]
#[allow(for_loops_over_fallibles)]
pub fn test_iter_mut() {
    let mut ok: Result<isize, &'static str> = Ok(100);
    for loc in ok.iter_mut() {
        *loc = 200;
    }
    assert_eq!(ok, Ok(200));
    for loc in &mut ok {
        *loc = 300;
    }
    assert_eq!(ok, Ok(300));

    let mut err: Result<isize, &'static str> = Err("error");
    for loc in err.iter_mut() {
        *loc = 200;
    }
    assert_eq!(err, Err("error"));
}

#[test]
pub fn test_unwrap_or_default() {
    assert_eq!(op1().unwrap_or_default(), 666);
    assert_eq!(op2().unwrap_or_default(), 0);
}

#[test]
#[allow(non_local_definitions)]
pub fn test_into_ok() {
    fn infallible_op() -> Result<isize, !> {
        Ok(666)
    }

    assert_eq!(infallible_op().into_ok(), 666);

    enum MyNeverToken {}
    impl From<MyNeverToken> for ! {
        fn from(never: MyNeverToken) -> ! {
            match never {}
        }
    }

    fn infallible_op2() -> Result<isize, MyNeverToken> {
        Ok(667)
    }

    assert_eq!(infallible_op2().into_ok(), 667);
}

#[test]
#[allow(non_local_definitions)]
pub fn test_into_err() {
    fn until_error_op() -> Result<!, isize> {
        Err(666)
    }

    assert_eq!(until_error_op().into_err(), 666);

    enum MyNeverToken {}
    impl From<MyNeverToken> for ! {
        fn from(never: MyNeverToken) -> ! {
            match never {}
        }
    }

    fn until_error_op2() -> Result<MyNeverToken, isize> {
        Err(667)
    }

    assert_eq!(until_error_op2().into_err(), 667);
}

#[test]
fn test_try() {
    fn try_result_ok() -> Result<u8, u32> {
        let result: Result<u8, u8> = Ok(1);
        let val = result?;
        Ok(val)
    }
    assert_eq!(try_result_ok(), Ok(1));

    fn try_result_err() -> Result<u8, u32> {
        let result: Result<u8, u8> = Err(1);
        let val = result?;
        Ok(val)
    }
    assert_eq!(try_result_err(), Err(1));
}

#[test]
fn test_result_as_deref() {
    // &Result<T: Deref, E>::Ok(T).as_deref() ->
    //      Result<&T::Deref::Target, &E>::Ok(&*T)
    let ref_ok = &Result::Ok::<&i32, u8>(&42);
    let expected_result = Result::Ok::<&i32, &u8>(&42);
    assert_eq!(ref_ok.as_deref(), expected_result);

    let ref_ok = &Result::Ok::<String, u32>(String::from("a result"));
    let expected_result = Result::Ok::<&str, &u32>("a result");
    assert_eq!(ref_ok.as_deref(), expected_result);

    let ref_ok = &Result::Ok::<Vec<i32>, u32>(vec![1, 2, 3, 4, 5]);
    let expected_result = Result::Ok::<&[i32], &u32>([1, 2, 3, 4, 5].as_slice());
    assert_eq!(ref_ok.as_deref(), expected_result);

    // &Result<T: Deref, E>::Err(T).as_deref() ->
    //      Result<&T::Deref::Target, &E>::Err(&*E)
    let val = 41;
    let ref_err = &Result::Err::<&u8, i32>(val);
    let expected_result = Result::Err::<&u8, &i32>(&val);
    assert_eq!(ref_err.as_deref(), expected_result);

    let s = String::from("an error");
    let ref_err = &Result::Err::<&u32, String>(s.clone());
    let expected_result = Result::Err::<&u32, &String>(&s);
    assert_eq!(ref_err.as_deref(), expected_result);

    let v = vec![5, 4, 3, 2, 1];
    let ref_err = &Result::Err::<&u32, Vec<i32>>(v.clone());
    let expected_result = Result::Err::<&u32, &Vec<i32>>(&v);
    assert_eq!(ref_err.as_deref(), expected_result);
}

#[test]
fn test_result_as_deref_mut() {
    // &mut Result<T: DerefMut, E>::Ok(T).as_deref_mut() ->
    //      Result<&mut T::DerefMut::Target, &mut E>::Ok(&mut *T)
    let mut val = 42;
    let mut expected_val = 42;
    let mut_ok = &mut Result::Ok::<&mut i32, u8>(&mut val);
    let expected_result = Result::Ok::<&mut i32, &mut u8>(&mut expected_val);
    assert_eq!(mut_ok.as_deref_mut(), expected_result);

    let mut expected_string = String::from("a result");
    let mut_ok = &mut Result::Ok::<String, u32>(expected_string.clone());
    let expected_result = Result::Ok::<&mut str, &mut u32>(expected_string.deref_mut());
    assert_eq!(mut_ok.as_deref_mut(), expected_result);

    let mut expected_vec = vec![1, 2, 3, 4, 5];
    let mut_ok = &mut Result::Ok::<Vec<i32>, u32>(expected_vec.clone());
    let expected_result = Result::Ok::<&mut [i32], &mut u32>(expected_vec.as_mut_slice());
    assert_eq!(mut_ok.as_deref_mut(), expected_result);

    // &mut Result<T: DerefMut, E>::Err(T).as_deref_mut() ->
    //      Result<&mut T, &mut E>::Err(&mut *E)
    let mut val = 41;
    let mut_err = &mut Result::Err::<&mut u8, i32>(val);
    let expected_result = Result::Err::<&mut u8, &mut i32>(&mut val);
    assert_eq!(mut_err.as_deref_mut(), expected_result);

    let mut expected_string = String::from("an error");
    let mut_err = &mut Result::Err::<&mut u32, String>(expected_string.clone());
    let expected_result = Result::Err::<&mut u32, &mut String>(&mut expected_string);
    assert_eq!(mut_err.as_deref_mut(), expected_result);

    let mut expected_vec = vec![5, 4, 3, 2, 1];
    let mut_err = &mut Result::Err::<&mut u32, Vec<i32>>(expected_vec.clone());
    let expected_result = Result::Err::<&mut u32, &mut Vec<i32>>(&mut expected_vec);
    assert_eq!(mut_err.as_deref_mut(), expected_result);
}

#[test]
fn result_const() {
    // test that the methods of `Result` are usable in a const context

    const RESULT: Result<usize, bool> = Ok(32);

    const REF: Result<&usize, &bool> = RESULT.as_ref();
    assert_eq!(REF, Ok(&32));

    const IS_OK: bool = RESULT.is_ok();
    assert!(IS_OK);

    const IS_ERR: bool = RESULT.is_err();
    assert!(!IS_ERR)
}

#[test]
const fn result_const_mut() {
    let mut result: Result<usize, bool> = Ok(32);

    {
        let as_mut = result.as_mut();
        match as_mut {
            Ok(v) => *v = 42,
            Err(_) => unreachable!(),
        }
    }

    let mut result_err: Result<usize, bool> = Err(false);

    {
        let as_mut = result_err.as_mut();
        match as_mut {
            Ok(_) => unreachable!(),
            Err(v) => *v = true,
        }
    }
}

#[test]
fn result_opt_conversions() {
    #[derive(Copy, Clone, Debug, PartialEq)]
    struct BadNumErr;

    fn try_num(x: i32) -> Result<i32, BadNumErr> {
        if x <= 5 { Ok(x + 1) } else { Err(BadNumErr) }
    }

    type ResOpt = Result<Option<i32>, BadNumErr>;
    type OptRes = Option<Result<i32, BadNumErr>>;

    let mut x: ResOpt = Ok(Some(5));
    let mut y: OptRes = Some(Ok(5));
    assert_eq!(x, y.transpose());
    assert_eq!(x.transpose(), y);

    x = Ok(None);
    y = None;
    assert_eq!(x, y.transpose());
    assert_eq!(x.transpose(), y);

    x = Err(BadNumErr);
    y = Some(Err(BadNumErr));
    assert_eq!(x, y.transpose());
    assert_eq!(x.transpose(), y);

    let res: Result<Vec<i32>, BadNumErr> = (0..10)
        .map(|x| {
            let y = try_num(x)?;
            Ok(if y % 2 == 0 { Some(y - 1) } else { None })
        })
        .filter_map(Result::transpose)
        .collect();

    assert_eq!(res, Err(BadNumErr))
}

#[test]
fn result_try_trait_v2_branch() {
    use core::num::NonZero;
    use core::ops::ControlFlow::*;
    use core::ops::Try;

    assert_eq!(Ok::<i32, i32>(4).branch(), Continue(4));
    assert_eq!(Err::<i32, i32>(4).branch(), Break(Err(4)));
    let one = NonZero::new(1).unwrap();
    assert_eq!(Ok::<(), NonZero<u32>>(()).branch(), Continue(()));
    assert_eq!(Err::<(), NonZero<u32>>(one).branch(), Break(Err(one)));
    assert_eq!(Ok::<NonZero<u32>, ()>(one).branch(), Continue(one));
    assert_eq!(Err::<NonZero<u32>, ()>(()).branch(), Break(Err(())));
}

// helper functions for const contexts
const fn eq10(x: u8) -> bool {
    x == 10
}
const fn eq20(e: u8) -> bool {
    e == 20
}
const fn double_u16(x: u8) -> u16 {
    x as u16 * 2
}
const fn to_u16(x: u8) -> u16 {
    x as u16
}
const fn err_to_u16_plus1(e: u8) -> u16 {
    e as u16 + 1
}
const fn inc_u8(x: u8) -> u8 {
    x + 1
}
const fn noop_u8_ref(_x: &u8) {}
const fn add1_result(x: u8) -> Result<u8, u8> {
    Ok(x + 1)
}
const fn add5_result(e: u8) -> Result<u8, u8> {
    Ok(e + 5)
}
const fn plus7_u8(e: u8) -> u8 {
    e + 7
}

#[test]
fn test_const_result() {
    const {
        let r_ok: Result<u8, u8> = Ok(10);
        let r_err: Result<u8, u8> = Err(20);
        assert!(r_ok.is_ok());
        assert!(r_err.is_err());

        let ok_and = r_ok.is_ok_and(eq10);
        let err_and = r_err.is_err_and(eq20);
        assert!(ok_and);
        assert!(err_and);

        let opt_ok: Option<u8> = r_ok.ok();
        let opt_err: Option<u8> = r_err.err();
        assert!(opt_ok.unwrap() == 10);
        assert!(opt_err.unwrap() == 20);

        let mapped: Result<u16, u8> = r_ok.map(double_u16);
        let map_or: u16 = r_ok.map_or(0, to_u16);
        let map_or_else: u16 = r_err.map_or_else(err_to_u16_plus1, to_u16);
        let map_or_default: u8 = r_err.map_or_default(inc_u8);
        assert!(mapped.unwrap_or_default() == 20);
        assert!(map_or == 10);
        assert!(map_or_else == 21);
        assert!(map_or_default == 0);

        let _map_err: Result<u8, u16> = r_err.map_err(to_u16);
        //FIXME: currently can't unwrap const error
        // assert!(map_err.unwrap_err() == 20);

        let inspected_ok: Result<u8, u8> = r_ok.inspect(noop_u8_ref);
        let inspected_err: Result<u8, u8> = r_err.inspect_err(noop_u8_ref);
        assert!(inspected_ok.is_ok());
        assert!(inspected_err.is_err());

        let unwrapped_default: u8 = r_err.unwrap_or_default();
        assert!(unwrapped_default == 0);

        let and_then: Result<u8, u8> = r_ok.and_then(add1_result);
        let or: Result<u8, u8> = r_err.or(Ok(5));
        let or_else: Result<u8, u8> = r_err.or_else(add5_result);
        assert!(and_then.unwrap_or_default() == 11);
        assert!(or.unwrap_or_default() == 5);
        assert!(or_else.unwrap_or_default() == 25);

        let u_or: u8 = r_err.unwrap_or(7);
        let u_or_else: u8 = r_err.unwrap_or_else(plus7_u8);
        assert!(u_or == 7);
        assert!(u_or_else == 27);
    };
}
