use std::collections::HashMap;

use specta::{ResolvedTypes, Type, Types};
use specta_typescript::Typescript;

#[derive(Type)]
pub struct TypeOne {
    pub field1: String,
    pub field2: i32,
    pub test: TypeTwo,
}

#[derive(Type)]
pub struct TypeTwo {
    pub field1: String,
    pub field2: i32,
    pub field3: GenericType<()>,
    pub field4: GenericType<i32>,
}

#[derive(Type)]
#[specta(transparent)]
pub struct TransparentWithSkip(#[specta(skip)] (), String);

#[derive(Type)]
pub struct GenericType<A> {
    pub my_field: String,
    pub generic: A,
}

#[derive(Type, Hash)]
pub enum MyEnum {
    A,
    B,
    C,
}

#[derive(Type)]
pub struct Something {
    a: HashMap<MyEnum, i32>,
}

// #[derive(Type)]
// pub struct Recursive {
//     a: Box<Recursive>,
// }

#[derive(Type)]
#[specta(collect = false)]
struct B {
    b: u32,
    c: C,
}

#[derive(Type)]
#[specta(collect = false)]
struct C {
    b: u32,
}

#[derive(Type)]
#[specta(collect = false)]
struct A {
    a: B,
    c: C,
    #[specta(inline)]
    d: C,
}

pub struct Selection {
    pub name: String,
    pub age: i32,
    pub password: String,
}

pub type Alias<T> = Option<T>;

pub struct Lol<A> {
    pub a: Alias<A>, // TODO: The best the macro can do is to infer this as `A`. It can't see `Option<A>`.
}

fn main() {
    // let s = Selection {
    //     name: "Monty Beaumont".into(),
    //     age: 7,
    //     password: "password123".into(),
    // };
    // let s1 = specta_util::selection!(s, { name, age });
    // todo(&s1);
    // return;

    // println!("{:#?}", <[String; 5] as Type>::definition(&mut Types::default()));
    // println!("{:#?}", datatype::inline::<[String; 5]>(&mut Types::default()));
    // println!("{:?}", specta_typescript::legacy::inline::<[String; 5]>(&Default::default()));

    // println!("{:?}", specta_typescript::legacy::inline::<A>(&Default::default()));
    // println!("{:?}", specta_typescript::export::<A>(&Default::default()));

    let types = Types::default()
        .register::<TypeOne>()
        // notice how we don't list `TypeTwo`. It's a dependency of `TypeOne` and will be exported automatically.
        .register::<TransparentWithSkip>()
        .register::<GenericType<String>>()
        .register::<MyEnum>()
        .register::<Something>()
        // .register::<Recursive>()
        .register::<A>();

    // TODO: You should use `specta_serde`

    // TODO: Remove this stuff
    // {
    //     let dt = specta::datatype::inline::<TypeOne>(&mut types);
    //     println!("{:?}", specta_typescript::datatype(&Typescript::default(), &FunctionResultVariant::Value(dt), &types));

    //     // let dt = specta::datatype::reference::inline::<TypeOne>(&mut types, &[]);
    //     println!("{:?}\n\n", specta_typescript::legacy::inline::<TypeOne>(&Default::default()));
    // }

    Typescript::default()
        .export_to("./bindings.ts", &ResolvedTypes::from_resolved_types(types))
        .unwrap();

    let result = std::fs::read_to_string("./bindings.ts").unwrap();
    println!("{result}");
    //     assert_eq!(result, r#"// This file has been generated by Specta. DO NOT EDIT.

    // export type TypeOne = { field1: string; field2: number; test: TypeTwo }

    // export type TypeTwo = { field1: string; field2: number }

    // "#);
}
