/* The lowfi UI:
┌─────────────────────────────┐
│ loading                     │
│  [           ] 00:00/00:00  │
│ [s]kip    [p]ause    [q]uit │
└─────────────────────────────┘
*/

#[cfg(test)]
mod components {
    use crate::ui::interface;

    use std::time::Duration;

    #[test]
    fn format_duration_works() {
        let d = Duration::from_secs(62);
        assert_eq!(interface::components::format_duration(&d), "01:02");
    }

    #[test]
    fn format_duration_zero() {
        let d = Duration::from_secs(0);
        assert_eq!(interface::components::format_duration(&d), "00:00");
    }

    #[test]
    fn format_duration_hours_wrap() {
        let d = Duration::from_secs(3661); // 1:01:01
        assert_eq!(interface::components::format_duration(&d), "61:01");
    }

    #[test]
    fn audio_bar_contains_percentage() {
        let s = interface::components::audio_bar(10, 0.5, "50%");
        assert!(s.contains("50%"));
        assert!(s.starts_with(" volume:"));
    }

    #[test]
    fn audio_bar_muted_volume() {
        let s = interface::components::audio_bar(8, 0.0, "0%");
        assert!(s.contains("0%"));
    }

    #[test]
    fn audio_bar_full_volume() {
        let s = interface::components::audio_bar(10, 1.0, "100%");
        assert!(s.contains("100%"));
    }

    #[test]
    fn controls_has_items() {
        let s = interface::components::controls(30);
        assert!(s.contains("[s]"));
        assert!(s.contains("[p]"));
        assert!(s.contains("[q]"));
    }
}

#[cfg(test)]
mod window {
    use crate::ui::interface::Window;

    #[test]
    fn new_border_strings() {
        let w = Window::new(10, false, false, false);
        assert!(w.titlebar.content.starts_with('┌'));
        assert!(w.statusbar.starts_with('└'));

        let w2 = Window::new(5, true, false, false);
        assert!(w2.titlebar.content.is_empty());
        assert!(w2.statusbar.is_empty());
    }

    fn sided(text: &str) -> String {
        return format!("│ {text} │");
    }

    #[test]
    fn simple() {
        let w = Window::new(3, false, false, false);
        let (render, height) = w.render(vec![String::from("abc")]).unwrap();

        const MIDDLE: &str = "─────";
        assert_eq!(format!("┌{MIDDLE}┐\n{}\n└{MIDDLE}┘", sided("abc")), render);
        assert_eq!(height, 3);
    }

    #[test]
    fn spaced() {
        let w = Window::new(3, false, true, false);
        let (render, height) = w
            .render(vec![
                String::from("abc"),
                String::from(" b"),
                String::from("c"),
            ])
            .unwrap();

        const MIDDLE: &str = "─────";
        assert_eq!(
            format!(
                "┌{MIDDLE}┐\n{}\n{}\n{}\n└{MIDDLE}┘",
                sided("abc"),
                sided(" b "),
                sided("c  "),
            ),
            render
        );
        assert_eq!(height, 5);
    }

    #[test]
    fn zero_width_window() {
        let w = Window::new(0, false, false, false);
        assert!(!w.titlebar.content.is_empty());
    }
}

#[cfg(test)]
mod interface {
    use crossterm::style::Stylize;
    use std::{sync::Arc, time::Duration};
    use tokio::time::Instant;

    use crate::{
        downloader::Progress,
        player::Current,
        tracks,
        ui::{Interface, State},
    };

    #[tokio::test]
    async fn loading() {
        let sink = Arc::new(rodio::Player::new().0);
        let mut state = State::initial(sink, String::from("test"));
        let menu = Interface::default().menu(&mut state);

        assert_eq!(menu[0], "loading                    ");
        assert_eq!(menu[1], " [           ] 00:00/00:00 ");
        assert_eq!(
            menu[2],
            format!(
                "{}kip    {}ause    {}uit",
                "[s]".bold(),
                "[p]".bold(),
                "[q]".bold()
            )
        );
    }

    #[tokio::test]
    async fn volume() {
        let sink = Arc::new(rodio::Player::new().0);
        sink.set_volume(0.5);

        let mut state = State::initial(sink, String::from("test"));
        state.volume_timer = Some(Instant::now());

        let menu = Interface::default().menu(&mut state);
        assert_eq!(menu[0], "loading                    ");
        assert_eq!(menu[1], " volume: [/////     ]  50% ");
        assert_eq!(
            menu[2],
            format!(
                "{}kip    {}ause    {}uit",
                "[s]".bold(),
                "[p]".bold(),
                "[q]".bold()
            )
        );
    }

    #[tokio::test]
    async fn progress() {
        let sink = Arc::new(rodio::Player::new().0);
        Progress::new().set(0.5);
        let mut state = State::initial(sink, String::from("test"));
        state.current = Current::Loading(Some(Progress::new()));

        let menu = Interface::default().menu(&mut state);

        assert_eq!(menu[0], format!("loading {}                ", "50%".bold()));
        assert_eq!(menu[1], " [           ] 00:00/00:00 ");
        assert_eq!(
            menu[2],
            format!(
                "{}kip    {}ause    {}uit",
                "[s]".bold(),
                "[p]".bold(),
                "[q]".bold()
            )
        );
    }

    #[tokio::test]
    async fn track() {
        let sink = Arc::new(rodio::Player::new().0);
        let track = tracks::Info {
            path: "/path".to_owned(),
            display: "Test Track".to_owned(),
            width: 4 + 1 + 5,
            duration: Some(Duration::from_secs(8)),
        };

        let mut state = State::initial(sink, String::from("test"));
        state.current = Current::Track(track.clone());
        let menu = Interface::default().menu(&mut state);

        assert_eq!(
            menu[0],
            format!("playing {}         ", track.display.bold())
        );
        assert_eq!(menu[1], " [           ] 00:00/00:08 ");
        assert_eq!(
            menu[2],
            format!(
                "{}kip    {}ause    {}uit",
                "[s]".bold(),
                "[p]".bold(),
                "[q]".bold()
            )
        );
    }
}
