use core::str::FromStr;

use beancount_types::AccountTemplate;
use miette::Diagnostic;
use snafu::Snafu;

#[derive(Clone, Debug)]
struct AnySelector(String);

impl FromStr for AnySelector {
    type Err = NeverSelectorError;

    fn from_str(value: &str) -> Result<Self, Self::Err> {
        Ok(Self(value.into()))
    }
}

#[derive(Debug, Diagnostic, Snafu)]
enum NeverSelectorError {}

#[derive(Clone, Copy, Debug)]
struct NoSelector();

impl FromStr for NoSelector {
    type Err = SelectorError;

    fn from_str(selector: &str) -> Result<Self, Self::Err> {
        SelectorSnafu { selector }.fail()
    }
}

#[derive(Debug, Diagnostic, Snafu)]
struct SelectorError {
    selector: String,
}

#[test]
fn parsing_empty_template_fails() {
    let template = AccountTemplate::<AnySelector>::parse("").unwrap_err();

    insta::assert_debug_snapshot!(template);
}

#[test]
fn parsing_only_base_account_works() {
    let template = AccountTemplate::<AnySelector>::parse("Assets:Banking").unwrap();

    insta::assert_debug_snapshot!(template);
}

#[test]
fn parsing_base_account_and_trailing_selector_works() {
    let template = AccountTemplate::<AnySelector>::parse("Assets:Banking:{institution}").unwrap();

    insta::assert_debug_snapshot!(template);
}

#[test]
fn parsing_base_account_and_multiple_trailing_selectors_works() {
    let template =
        AccountTemplate::<AnySelector>::parse("Assets:Banking:{institution}:{account}:{position}")
            .unwrap();

    insta::assert_debug_snapshot!(template);
}

#[test]
fn parsing_base_account_and_mixed_trailing_selectors_and_literals_works() {
    let template = AccountTemplate::<AnySelector>::parse(
        "Assets:Banking:{institution}:{account}:Stocks:{position}",
    )
    .unwrap();

    insta::assert_debug_snapshot!(template);
}

#[test]
fn parsing_fails_for_invalid_literals() {
    let template = AccountTemplate::<AnySelector>::parse(
        "Assets:Banking:{institution}:Main Account:stocks:{position}",
    )
    .unwrap_err();

    insta::assert_debug_snapshot!(template);
}

#[test]
fn parsing_fails_for_invalid_selectors() {
    let template = AccountTemplate::<NoSelector>::parse(
        "Assets:Banking:{institution}:{account}:Stocks:{position}",
    )
    .unwrap_err();

    insta::assert_debug_snapshot!(template);
}

#[test]
fn parsing_fails_for_invalid_base_account() {
    let template =
        AccountTemplate::<AnySelector>::parse("Assets:Banking:Bank of America:{account}")
            .unwrap_err();

    insta::assert_debug_snapshot!(template);
}

#[test]
fn multiple_parsing_errors_are_reported_in_bulk() {
    let template =
        AccountTemplate::<AnySelector>::parse("Assets:Banking:Bank of America:{account}:cash:{po")
            .unwrap_err();

    insta::assert_debug_snapshot!(template);
}