use std::{
    cmp::min,
    iter::zip,
    ops::{Sub, SubAssign},
};

use super::{Number, Sign};

impl Sub for Number {
    type Output = Self;

    fn sub(self, rhs: Self) -> Self::Output {
        let self_len = self.digits.len();
        let rhs_len = rhs.digits.len();
        let mut self_digits = self.digits.clone();
        let mut rhs_digits = rhs.digits.clone();
        if self_len != rhs_len {
            let difference = (self_len).abs_diff(rhs_len);
            let pad = vec![0i8; difference];
            if min(self_len, rhs_len) == self_len {
                self_digits = [self.digits, pad].concat();
            } else {
                rhs_digits = [rhs.digits, pad].concat();
            }
        }
        let zipped = zip(self_digits.iter(), rhs_digits.iter());
        let mut carry = 0;
        let mut digits = Vec::new();
        let mut sign = match (self.sign, rhs.sign) {
            (Sign::Positif, Sign::Negatif) => Sign::Positif,
            (Sign::Negatif, Sign::Positif) => Sign::Negatif,
            (Sign::Positif, Sign::Positif) | (Sign::Negatif, Sign::Negatif) => Sign::Positif,
        };
        for (a, b) in zipped {
            let comb = match (self.sign, rhs.sign) {
                (Sign::Positif, Sign::Positif) => {
                    let comb = a - b - carry;
                    if comb < 0 {
                        carry = 1;
                    } else {
                        carry = 0;
                    }
                    if b > a {
                        sign = Sign::Negatif;
                    } else {
                        sign = Sign::Positif;
                    }
                    (comb % 10).abs()
                }
                (Sign::Positif, Sign::Negatif) => {
                    let comb = a + b + carry;
                    if comb > 9 {
                        carry = 1;
                    } else {
                        carry = 0;
                    }
                    comb % 10
                }
                (Sign::Negatif, Sign::Positif) => {
                    let comb = -a - b - carry;
                    if comb < -9 {
                        carry = 1;
                    } else {
                        carry = 0;
                    }
                    (comb % 10).abs()
                }
                (Sign::Negatif, Sign::Negatif) => {
                    let comb = -a + b + carry;
                    if comb > 10 {
                        carry = 1;
                    } else {
                        carry = 0;
                    }
                    if a > b {
                        sign = Sign::Negatif;
                    } else {
                        sign = Sign::Positif;
                    }
                    comb % 10
                }
            };
            digits.push(comb);
        }
        if carry != 0 {
            digits.push(carry);
        }

        Self { digits, sign }
    }
}

impl SubAssign for Number {
    fn sub_assign(&mut self, rhs: Self) {
        let new = self.clone() - rhs;
        self.digits = new.digits;
        self.sign = new.sign;
    }
}

#[cfg(test)]
mod test_number_sub {
    use crate::number::Number;

    #[test]
    fn sub_positif() {
        let a = Number::from(1);
        let b = 1.into();
        let res = Number::from(0);
        assert_eq!(res, a - b);
    }

    #[test]
    fn sub_negatif() {
        let a = Number::from(-1);
        let b = (-1).into();
        let res = Number::from(0);
        assert_eq!(res, a - b);
    }

    #[test]
    fn sub_negatif_from_positif() {
        let a = Number::from(1);
        let b = (-1).into();
        let res = Number::from(2);
        assert_eq!(res, a - b);
    }

    #[test]
    fn sub_positif_from_negatif() {
        let a = Number::from(-1);
        let b = (1).into();
        let res = Number::from(-2);
        assert_eq!(res, a - b);
    }

    #[test]
    fn sub_larger_positif_from_negatif() {
        let a = Number::from(-1);
        let b = (11).into();
        let res = Number::from(-12);
        assert_eq!(res, a - b);
    }

    #[test]
    fn sub_larger_negatif_from_positif() {
        let a = Number::from(1);
        let b = (-11).into();
        let res = Number::from(12);
        assert_eq!(res, a - b);
    }

    #[test]
    fn sub_positif_from_larger_negatif() {
        let a = Number::from(-99);
        let b = (11).into();
        let res = Number::from(-110);
        assert_eq!(res, a - b);
    }

    #[test]
    fn sub_negatif_from_larger_positif() {
        let a = Number::from(99);
        let b = (-11).into();
        let res = Number::from(110);
        assert_eq!(res, a - b);
    }
}