extern crate alloc;

use super::Str;
use alloc::{
    string::{String, ToString},
    vec,
    vec::Vec,
};

#[test]
fn new_starts_empty() {
    let s = Str::<8>::new();
    assert_eq!(s.capacity(), 8);
    assert_eq!(s.len(), 0);
    assert!(s.is_empty());
    assert_eq!(s.as_str(), "");
}

#[test]
fn from_str_truncates_to_capacity() {
    let s = Str::<4>::from_str("abcdef");
    assert_eq!(s.len(), 4);
    assert_eq!(s.as_str(), "abcd");
}

#[test]
fn vec_of_string_collects_into_vec_of_str() {
    let input = vec![
        String::from("alpha"),
        String::from("beta"),
        String::from("gamma"),
    ];

    let out: Vec<Str<16>> = input
        .into_iter()
        .map(|s| Str::<16>::from(s.as_str()))
        .collect();

    assert_eq!(out.len(), 3);
    assert_eq!(out[0].as_str(), "alpha");
    assert_eq!(out[1].as_str(), "beta");
    assert_eq!(out[2].as_str(), "gamma");
}

#[test]
#[should_panic(expected = "stx::Str capacity exceeded")]
fn vec_of_string_panics_when_any_item_exceeds_capacity() {
    let input = vec![String::from("ok"), String::from("this is too long")];

    let _out: Vec<Str<8>> = input
        .into_iter()
        .map(|s| Str::<8>::from(s.as_str()))
        .collect();
}

#[test]
fn array_of_string_maps_into_array_of_str() {
    let input = [
        String::from("alpha"),
        String::from("beta"),
        String::from("gamma"),
    ];

    let out: [Str<16>; 3] = input.map(|s| Str::<16>::from(s.as_str()));

    assert_eq!(out[0].as_str(), "alpha");
    assert_eq!(out[1].as_str(), "beta");
    assert_eq!(out[2].as_str(), "gamma");
}

#[test]
fn vec_of_string_can_convert_into_fixed_array_via_try_into() {
    let input = vec![
        String::from("zero"),
        String::from("one"),
        String::from("two"),
        String::from("three"),
    ];

    let out: [Str<16>; 4] = input
        .into_iter()
        .map(|s| Str::<16>::from(s.as_str()))
        .collect::<Vec<_>>()
        .try_into()
        .expect("expected exactly 4 items");

    assert_eq!(out[0].as_str(), "zero");
    assert_eq!(out[1].as_str(), "one");
    assert_eq!(out[2].as_str(), "two");
    assert_eq!(out[3].as_str(), "three");
}

#[test]
fn vec_of_string_to_array_fails_when_length_does_not_match() {
    let input = vec![
        String::from("zero"),
        String::from("one"),
        String::from("two"),
    ];

    let out: Result<[Str<16>; 4], Vec<Str<16>>> = input
        .into_iter()
        .map(|s| Str::<16>::from(s.as_str()))
        .collect::<Vec<_>>()
        .try_into();

    assert!(out.is_err());
}

#[test]
fn push_str_respects_capacity() {
    let mut s = Str::<5>::from("hi");
    assert!(s.push_str("!").is_ok());
    let err = s.push_str("there").unwrap_err();
    assert_eq!(err.len, 8);
    assert_eq!(err.capacity, 5);
    assert_eq!(s.as_str(), "hi!");
}

#[test]
fn try_from_str_reports_capacity_error() {
    let err = Str::<3>::try_from_str("hello").unwrap_err();
    assert_eq!(err.len, 5);
    assert_eq!(err.capacity, 3);
    assert_eq!(
        err.to_string(),
        "stx::Str capacity exceeded: len 5 > capacity 3"
    );
}

#[test]
fn truncate_and_clear_update_length() {
    let mut s = Str::<8>::from("abcdef");
    s.truncate(3);
    assert_eq!(s.as_str(), "abc");
    assert_eq!(s.len(), 3);

    s.clear();
    assert!(s.is_empty());
    assert_eq!(s.as_str(), "");
}

#[test]
fn deref_mut_allows_mutating_str_methods() {
    let mut s = Str::<8>::from("hello");
    s.make_ascii_uppercase();
    assert_eq!(s.as_str(), "HELLO");
}

#[test]
#[should_panic(expected = "stx::Str capacity exceeded")]
fn from_str_panics_when_capacity_exceeded() {
    let _ = Str::<3>::from("hello");
}

#[cfg(feature = "serde")]
#[test]
fn serializes_as_json_string() {
    let value = Str::<8>::from("hello");
    let json = serde_json::to_string(&value).unwrap();
    assert_eq!(json, "\"hello\"");
}

#[cfg(feature = "serde")]
#[test]
fn deserializes_from_json_string() {
    let value: Str<8> = serde_json::from_str("\"hello\"").unwrap();
    assert_eq!(value.as_str(), "hello");
}

#[cfg(feature = "serde")]
#[test]
fn deserialization_rejects_over_capacity() {
    let err = serde_json::from_str::<Str<3>>("\"hello\"").unwrap_err();
    assert!(err.is_data());
}
