use chrono::Weekday;
use nom::{
    Parser,
    branch::alt,
    bytes::complete::tag_no_case,
    character::complete::space1,
    combinator::{opt, value},
    sequence::preceded,
};

use crate::error::FlexitimeResult2;

#[derive(Debug, Clone, PartialEq)]
pub enum DayOffset {
    Fixed(i32),
    NextDayOccurrence(Weekday),
}

pub fn parse_day_offset(input: &str) -> FlexitimeResult2<&str, DayOffset> {
    alt((
        value(DayOffset::Fixed(1), tag_no_case("tomorrow")),
        value(DayOffset::Fixed(-1), tag_no_case("yesterday")),
        preceded(
            opt((alt((tag_no_case("this"), tag_no_case("next"))), space1)),
            alt((
                value(
                    DayOffset::NextDayOccurrence(Weekday::Mon),
                    alt((tag_no_case("monday"), tag_no_case("mon"))),
                ),
                value(
                    DayOffset::NextDayOccurrence(Weekday::Tue),
                    alt((tag_no_case("tuesday"), tag_no_case("tue"))),
                ),
                value(
                    DayOffset::NextDayOccurrence(Weekday::Wed),
                    alt((tag_no_case("wednesday"), tag_no_case("wed"))),
                ),
                value(
                    DayOffset::NextDayOccurrence(Weekday::Thu),
                    alt((tag_no_case("thursday"), tag_no_case("thu"))),
                ),
                value(
                    DayOffset::NextDayOccurrence(Weekday::Fri),
                    alt((tag_no_case("friday"), tag_no_case("fri"))),
                ),
                value(
                    DayOffset::NextDayOccurrence(Weekday::Sat),
                    alt((tag_no_case("saturday"), tag_no_case("sat"))),
                ),
                value(
                    DayOffset::NextDayOccurrence(Weekday::Sun),
                    alt((tag_no_case("sunday"), tag_no_case("sun"))),
                ),
            )),
        ),
    ))
    .parse(input)
}

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

    #[test]
    fn test_parse_weekday() {
        assert_eq!(
            parse_day_offset("monday"),
            Ok(("", DayOffset::NextDayOccurrence(Weekday::Mon)))
        );
        assert_eq!(
            parse_day_offset("tue"),
            Ok(("", DayOffset::NextDayOccurrence(Weekday::Tue)))
        );
    }

    #[test]
    fn test_parse_weekday_with_prefix() {
        assert_eq!(
            parse_day_offset("this wednesday"),
            Ok(("", DayOffset::NextDayOccurrence(Weekday::Wed)))
        );
        assert_eq!(
            parse_day_offset("next thursday"),
            Ok(("", DayOffset::NextDayOccurrence(Weekday::Thu)))
        );
    }

    #[test]
    fn test_parse_yesterday() {
        assert_eq!(
            parse_day_offset("yesterday"),
            Ok(("", DayOffset::Fixed(-1)))
        );
    }

    #[test]
    fn test_parse_tomorrow() {
        assert_eq!(parse_day_offset("tomorrow"), Ok(("", DayOffset::Fixed(1))));
    }
}
