use std::fmt::Display;
use std::path::Path;

use color_eyre::eyre::Result;
use tracing::info;
use tracing_subscriber::EnvFilter;

pub trait Solution<Input, Output: Display> {
    fn day() -> &'static str;

    fn parse_input<P: AsRef<Path>>(pathname: P) -> Input;

    fn part_1(input: &Input) -> Output;
    fn part_2(input: &Input) -> Output;

    fn main() -> Result<()>
    where
        Self: Sized,
    {
        tracing_subscriber::fmt()
            .with_env_filter(EnvFilter::from_default_env())
            .with_target(false)
            .with_file(true)
            .with_line_number(true)
            .without_time()
            .compact()
            .init();
        color_eyre::install()?;

        let input = Self::parse_input(format!("inputs/day{}.txt", Self::day()));

        info!("Part 1: {}", Self::part_1(&input));
        info!("Part 2: {}", Self::part_2(&input));

        Ok(())
    }
}

#[macro_export]
macro_rules! test_sample {
    ($mod_name:ident, $day_struct:tt, $part_1:expr, $part_2:expr) => {
        #[cfg(test)]
        mod $mod_name {
            use lazy_static::lazy_static;

            use super::*;

            lazy_static! {
                static ref SAMPLE: Input =
                    $day_struct::parse_input(&format!("samples/day{}.txt", $day_struct::day()));
            }

            #[test]
            fn test_part_1() {
                assert_eq!($day_struct::part_1(&SAMPLE), $part_1);
            }

            #[test]
            fn test_part_2() {
                assert_eq!($day_struct::part_2(&SAMPLE), $part_2);
            }
        }
    };
}