use super::parse_error::ParseError;
use crate::Interval;
use crate::interval_norm::IntervalNorm;

impl Interval {
    pub fn from_sql(sql_str: &str) -> Result<Interval, ParseError> {
        if sql_str == "0" {
            return Ok(Interval::new(0, 0, 0));
        }

        let tokens: Vec<&str> = sql_str.split_whitespace().collect();
        let mut interval_norm = IntervalNorm::default();

        match tokens.len() {
            1 => {
                let token = tokens[0];
                if token.contains(':') {
                    parse_time_part(token, &mut interval_norm, true)?;
                } else if token.contains('-') {
                    parse_year_month_part(token, &mut interval_norm)?;
                } else {
                    return Err(ParseError::from_invalid_interval(
                        "Invalid format: expected year-month or time format",
                    ));
                }
            }
            2 => {
                parse_day_part(tokens[0], &mut interval_norm)?;
                parse_time_part(tokens[1], &mut interval_norm, true)?;
            }
            3 => {
                // Mixed format: year-month + day + time
                parse_year_month_part(tokens[0], &mut interval_norm)?;
                parse_day_part(tokens[1], &mut interval_norm)?;
                parse_time_part(tokens[2], &mut interval_norm, false)?;
            }
            _ => {
                return Err(ParseError::from_invalid_interval(
                    "Invalid format: expected 1-3 tokens",
                ));
            }
        }

        interval_norm.try_into_interval()
    }
}

fn parse_year_month_part(token: &str, interval: &mut IntervalNorm) -> Result<(), ParseError> {
    let (sign, rest) = if let Some(stripped) = token.strip_prefix('-') {
        (-1, stripped)
    } else {
        (1, token.trim_start_matches('+'))
    };

    let (years_str, months_str) = if let Some(pos) = rest.find('-') {
        (&rest[..pos], &rest[pos + 1..])
    } else {
        return Err(ParseError::from_invalid_interval(
            "Invalid year-month format",
        ));
    };

    let years = if years_str.is_empty() {
        0
    } else {
        years_str.parse::<i32>()? * sign
    };

    let months: i32 = months_str.parse::<i32>()? * sign;

    interval.years = years;
    interval.months = months;
    Ok(())
}

fn parse_day_part(token: &str, interval: &mut IntervalNorm) -> Result<(), ParseError> {
    // Handle optional leading sign
    let (sign, days_str) = if let Some(rest) = token.strip_prefix('-') {
        (-1, rest)
    } else if let Some(rest) = token.strip_prefix('+') {
        (1, rest)
    } else {
        (1, token)
    };
    let days: i32 = days_str.parse()?;
    interval.days = days * sign;
    Ok(())
}

fn parse_time_part(
    token: &str,
    interval: &mut IntervalNorm,
    is_only_time: bool,
) -> Result<(), ParseError> {
    let (time_token, sign) = if is_only_time {
        let is_negative = token.starts_with('-');
        let token_str = if is_negative { &token[1..] } else { token };
        (token_str, if is_negative { -1 } else { 1 })
    } else {
        let is_negative = token.starts_with('-');
        let token_str = token.trim_start_matches('+').trim_start_matches('-');
        (token_str, if is_negative { -1 } else { 1 })
    };

    let time_parts: Vec<&str> = time_token.split(':').collect();

    if time_parts.len() < 2 || time_parts.len() > 3 {
        return Err(ParseError::from_invalid_interval("Invalid time format"));
    }

    let hours: i64 = time_parts[0].parse()?;
    let minutes: i64 = time_parts[1].parse()?;

    let (seconds, microseconds) = if time_parts.len() == 3 {
        parse_seconds_part(time_parts[2])?
    } else {
        (0, 0)
    };

    interval.hours = hours * sign;
    interval.minutes = minutes * sign;
    interval.seconds = seconds * sign;
    interval.microseconds = microseconds * sign;
    Ok(())
}

fn parse_seconds_part(token: &str) -> Result<(i64, i64), ParseError> {
    if token.contains('.') {
        let parts: Vec<&str> = token.split('.').collect();
        if parts.len() != 2 {
            return Err(ParseError::from_invalid_interval("Invalid seconds format"));
        }
        let seconds: i64 = parts[0].parse()?;
        let mut micros_str = parts[1].to_string();

        if micros_str.len() > 6 {
            return Err(ParseError::from_invalid_interval(
                "Microseconds precision too high",
            ));
        }

        while micros_str.len() < 6 {
            micros_str.push('0');
        }

        let microseconds: i64 = micros_str.parse()?;
        Ok((seconds, microseconds))
    } else {
        let seconds: i64 = token.parse()?;
        Ok((seconds, 0))
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_from_sql_1() {
        let interval = Interval::from_sql("1-0").unwrap();
        let interval_exp = Interval::new(12, 0, 0);
        assert_eq!(interval, interval_exp);
    }

    #[test]
    fn test_from_sql_2() {
        let interval = Interval::from_sql("1-1").unwrap();
        let interval_exp = Interval::new(13, 0, 0);
        assert_eq!(interval, interval_exp);
    }

    #[test]
    fn test_from_sql_3() {
        let interval = Interval::from_sql("+1-1 +1 +0:00:00").unwrap();
        let interval_exp = Interval::new(13, 1, 0);
        assert_eq!(interval, interval_exp);
    }

    #[test]
    fn test_from_sql_4() {
        let interval = Interval::from_sql("+1-1 +1 +1:00:00").unwrap();
        let interval_exp = Interval::new(13, 1, 3600000000);
        assert_eq!(interval, interval_exp);
    }

    #[test]
    fn test_from_sql_5() {
        let interval = Interval::from_sql("+1-1 +1 +1:10:00").unwrap();
        let interval_exp = Interval::new(13, 1, 4200000000);
        assert_eq!(interval, interval_exp);
    }

    #[test]
    fn test_from_sql_6() {
        let interval = Interval::from_sql("+1-1 +1 +1:10:15").unwrap();
        let interval_exp = Interval::new(13, 1, 4215000000);
        assert_eq!(interval, interval_exp);
    }

    #[test]
    fn test_from_sql_7() {
        let interval = Interval::from_sql("1:00:00").unwrap();
        let interval_exp = Interval::new(0, 0, 3600000000);
        assert_eq!(interval, interval_exp);
    }

    #[test]
    fn test_from_sql_8() {
        let interval = Interval::from_sql("1:10:00").unwrap();
        let interval_exp = Interval::new(0, 0, 4200000000);
        assert_eq!(interval, interval_exp);
    }

    #[test]
    fn test_from_sql_9() {
        let interval = Interval::from_sql("1:10:15").unwrap();
        let interval_exp = Interval::new(0, 0, 4215000000);
        assert_eq!(interval, interval_exp);
    }

    #[test]
    fn test_from_sql_10() {
        let interval = Interval::from_sql("-1-0").unwrap();
        let interval_exp = Interval::new(-12, 0, 0);
        assert_eq!(interval, interval_exp);
    }

    #[test]
    fn test_from_sql_11() {
        let interval = Interval::from_sql("-1-1").unwrap();
        let interval_exp = Interval::new(-13, 0, 0);
        assert_eq!(interval, interval_exp);
    }

    #[test]
    fn test_from_sql_12() {
        let interval = Interval::from_sql("-1-1 -1 +0:00:00").unwrap();
        let interval_exp = Interval::new(-13, -1, 0);
        assert_eq!(interval, interval_exp);
    }

    #[test]
    fn test_from_sql_13() {
        let interval = Interval::from_sql("-1-1 -1 -1:00:00").unwrap();
        let interval_exp = Interval::new(-13, -1, -3600000000);
        assert_eq!(interval, interval_exp);
    }

    #[test]
    fn test_from_sql_14() {
        let interval = Interval::from_sql("-1-1 -1 -1:10:00").unwrap();
        let interval_exp = Interval::new(-13, -1, -4200000000);
        assert_eq!(interval, interval_exp);
    }

    #[test]
    fn test_from_sql_15() {
        let interval = Interval::from_sql("-1-1 -1 -1:10:15").unwrap();
        let interval_exp = Interval::new(-13, -1, -4215000000);
        assert_eq!(interval, interval_exp);
    }

    #[test]
    fn test_from_sql_16() {
        let interval = Interval::from_sql("-1:00:00").unwrap();
        let interval_exp = Interval::new(0, 0, -3600000000);
        assert_eq!(interval, interval_exp);
    }

    #[test]
    fn test_from_sql_17() {
        let interval = Interval::from_sql("-1:10:00").unwrap();
        let interval_exp = Interval::new(0, 0, -4200000000);
        assert_eq!(interval, interval_exp);
    }

    #[test]
    fn test_from_sql_18() {
        let interval = Interval::from_sql("-1:10:15").unwrap();
        let interval_exp = Interval::new(0, 0, -4215000000);
        assert_eq!(interval, interval_exp);
    }

    #[test]
    fn test_from_sql_19() {
        let interval = Interval::from_sql("0").unwrap();
        let interval_exp = Interval::new(0, 0, 0);
        assert_eq!(interval, interval_exp);
    }

    #[test]
    fn test_from_sql_20() {
        let interval = Interval::from_sql("invalid");
        assert!(interval.is_err());
    }

    #[test]
    fn test_roundtrip_sql_1() {
        let original = Interval::new(12, 0, 0);
        let sql = original.to_sql();
        let parsed = Interval::from_sql(&sql).unwrap();
        assert_eq!(original, parsed);
    }

    #[test]
    fn test_roundtrip_sql_2() {
        let original = Interval::new(13, 0, 0);
        let sql = original.to_sql();
        let parsed = Interval::from_sql(&sql).unwrap();
        assert_eq!(original, parsed);
    }

    #[test]
    fn test_roundtrip_sql_3() {
        let original = Interval::new(13, 1, 0);
        let sql = original.to_sql();
        let parsed = Interval::from_sql(&sql).unwrap();
        assert_eq!(original, parsed);
    }

    #[test]
    fn test_roundtrip_sql_4() {
        let original = Interval::new(13, 1, 3600000000);
        let sql = original.to_sql();
        let parsed = Interval::from_sql(&sql).unwrap();
        assert_eq!(original, parsed);
    }

    #[test]
    fn test_roundtrip_sql_5() {
        let original = Interval::new(13, 1, 4200000000);
        let sql = original.to_sql();
        let parsed = Interval::from_sql(&sql).unwrap();
        assert_eq!(original, parsed);
    }

    #[test]
    fn test_roundtrip_sql_6() {
        let original = Interval::new(13, 1, 4215000000);
        let sql = original.to_sql();
        let parsed = Interval::from_sql(&sql).unwrap();
        assert_eq!(original, parsed);
    }

    #[test]
    fn test_roundtrip_sql_7() {
        let original = Interval::new(0, 0, 3600000000);
        let sql = original.to_sql();
        let parsed = Interval::from_sql(&sql).unwrap();
        assert_eq!(original, parsed);
    }

    #[test]
    fn test_roundtrip_sql_8() {
        let original = Interval::new(0, 0, 4200000000);
        let sql = original.to_sql();
        let parsed = Interval::from_sql(&sql).unwrap();
        assert_eq!(original, parsed);
    }

    #[test]
    fn test_roundtrip_sql_9() {
        let original = Interval::new(0, 0, 4215000000);
        let sql = original.to_sql();
        let parsed = Interval::from_sql(&sql).unwrap();
        assert_eq!(original, parsed);
    }

    #[test]
    fn test_roundtrip_sql_10() {
        let original = Interval::new(-12, 0, 0);
        let sql = original.to_sql();
        let parsed = Interval::from_sql(&sql).unwrap();
        assert_eq!(original, parsed);
    }

    #[test]
    fn test_roundtrip_sql_11() {
        let original = Interval::new(-13, 0, 0);
        let sql = original.to_sql();
        let parsed = Interval::from_sql(&sql).unwrap();
        assert_eq!(original, parsed);
    }

    #[test]
    fn test_roundtrip_sql_12() {
        let original = Interval::new(-13, -1, 0);
        let sql = original.to_sql();
        let parsed = Interval::from_sql(&sql).unwrap();
        assert_eq!(original, parsed);
    }

    #[test]
    fn test_roundtrip_sql_13() {
        let original = Interval::new(-13, -1, -3600000000);
        let sql = original.to_sql();
        let parsed = Interval::from_sql(&sql).unwrap();
        assert_eq!(original, parsed);
    }

    #[test]
    fn test_roundtrip_sql_14() {
        let original = Interval::new(-13, -1, -4200000000);
        let sql = original.to_sql();
        let parsed = Interval::from_sql(&sql).unwrap();
        assert_eq!(original, parsed);
    }

    #[test]
    fn test_roundtrip_sql_15() {
        let original = Interval::new(-13, -1, -4215000000);
        let sql = original.to_sql();
        let parsed = Interval::from_sql(&sql).unwrap();
        assert_eq!(original, parsed);
    }

    #[test]
    fn test_roundtrip_sql_16() {
        let original = Interval::new(0, 0, -3600000000);
        let sql = original.to_sql();
        let parsed = Interval::from_sql(&sql).unwrap();
        assert_eq!(original, parsed);
    }

    #[test]
    fn test_roundtrip_sql_17() {
        let original = Interval::new(0, 0, -4200000000);
        let sql = original.to_sql();
        let parsed = Interval::from_sql(&sql).unwrap();
        assert_eq!(original, parsed);
    }

    #[test]
    fn test_roundtrip_sql_18() {
        let original = Interval::new(0, 0, -4215000000);
        let sql = original.to_sql();
        let parsed = Interval::from_sql(&sql).unwrap();
        assert_eq!(original, parsed);
    }

    #[test]
    fn test_roundtrip_sql_19() {
        let original = Interval::new(0, 0, 0);
        let sql = original.to_sql();
        let parsed = Interval::from_sql(&sql).unwrap();
        assert_eq!(original, parsed);
    }

    #[test]
    fn test_roundtrip_sql_with_microseconds() {
        let original = Interval::new(1, 2, 123456);
        let sql = original.to_sql();
        let parsed = Interval::from_sql(&sql).unwrap();
        assert_eq!(original, parsed);
    }

    #[test]
    fn test_postgres_compatibility_year_month_only_positive() {
        let sql = "1-0";
        let interval = Interval::from_sql(sql).unwrap();
        assert_eq!(interval, Interval::new(12, 0, 0));
    }

    #[test]
    fn test_postgres_compatibility_year_month_only_negative() {
        let sql = "-1-1";
        let interval = Interval::from_sql(sql).unwrap();
        assert_eq!(interval, Interval::new(-13, 0, 0));
    }

    #[test]
    fn test_postgres_compatibility_time_only_positive() {
        let sql = "1:00:00";
        let interval = Interval::from_sql(sql).unwrap();
        assert_eq!(interval, Interval::new(0, 0, 3600000000));
    }

    #[test]
    fn test_postgres_compatibility_time_only_negative() {
        let sql = "-1:10:15";
        let interval = Interval::from_sql(sql).unwrap();
        assert_eq!(interval, Interval::new(0, 0, -4215000000));
    }

    #[test]
    fn test_postgres_compatibility_full_interval_positive() {
        let sql = "+1-1 +1 +1:10:15";
        let interval = Interval::from_sql(sql).unwrap();
        assert_eq!(interval, Interval::new(13, 1, 4215000000));
    }

    #[test]
    fn test_postgres_compatibility_full_interval_negative() {
        let sql = "-1-1 -1 -1:10:15";
        let interval = Interval::from_sql(sql).unwrap();
        assert_eq!(interval, Interval::new(-13, -1, -4215000000));
    }

    #[test]
    fn test_postgres_compatibility_fractional_seconds() {
        let sql = "1:10:15.123456";
        let interval = Interval::from_sql(sql).unwrap();
        assert_eq!(interval, Interval::new(0, 0, 4215123456));
    }

    #[test]
    fn test_postgres_compatibility_half_second() {
        let sql = "1:00:00.5";
        let interval = Interval::from_sql(sql).unwrap();
        assert_eq!(interval, Interval::new(0, 0, 3600500000));
    }

    #[test]
    fn test_postgres_compatibility_full_fractional_interval() {
        let sql = "+1-1 +1 +1:10:15.123456";
        let interval = Interval::from_sql(sql).unwrap();
        assert_eq!(interval, Interval::new(13, 1, 4215123456));
    }

    #[test]
    fn test_roundtrip_fractional_seconds() {
        let original = Interval::new(0, 0, 4215123456);
        let sql = original.to_sql();
        let parsed = Interval::from_sql(&sql).unwrap();
        assert_eq!(original, parsed);
    }

    #[test]
    fn test_roundtrip_fractional_with_months_days() {
        let original = Interval::new(13, 1, 4215123456);
        let sql = original.to_sql();
        let parsed = Interval::from_sql(&sql).unwrap();
        assert_eq!(original, parsed);
    }
}
