use crate::syntax::{
    ast::*,
    lexer::Lexer,
    FileParser,
};

fn parse(input: &str) -> File {
    let lexer = Lexer::new(input);
    let parser = FileParser::new();
    parser.parse(lexer).unwrap()
}

// ===== Sub-concept dot notation tests =====

#[test]
fn test_sub_concept_enum_dot_notation() {
    let input = "sub_concept Cup.Type { Small, Medium, Large }";
    let file = parse(input);

    assert_eq!(file.declarations.len(), 1);
    match &file.declarations[0] {
        | Declaration::SubConcept(sc) => {
            assert_eq!(sc.name, "Type");
            assert_eq!(sc.parent_concept, "Cup");
            match &sc.kind {
                | SubConceptKind::Enum { variants } => {
                    assert_eq!(variants, &["Small", "Medium", "Large"]);
                },
                | _ => panic!("Expected Enum sub-concept"),
            }
        },
        | _ => panic!("Expected SubConcept declaration"),
    }
}

#[test]
fn test_sub_concept_record_dot_notation() {
    let input = "sub_concept Cup.Material { weight: 100, fragile: true }";
    let file = parse(input);

    assert_eq!(file.declarations.len(), 1);
    match &file.declarations[0] {
        | Declaration::SubConcept(sc) => {
            assert_eq!(sc.name, "Material");
            assert_eq!(sc.parent_concept, "Cup");
            match &sc.kind {
                | SubConceptKind::Record { fields } => {
                    assert_eq!(fields.len(), 2);
                    assert_eq!(fields[0].name, "weight");
                    assert_eq!(fields[0].value, Value::Number(100));
                    assert_eq!(fields[1].name, "fragile");
                    assert_eq!(fields[1].value, Value::Boolean(true));
                },
                | _ => panic!("Expected Record sub-concept"),
            }
        },
        | _ => panic!("Expected SubConcept declaration"),
    }
}

#[test]
fn test_sub_concept_single_variant() {
    let input = "sub_concept Animal.Sound { Bark }";
    let file = parse(input);

    assert_eq!(file.declarations.len(), 1);
    match &file.declarations[0] {
        | Declaration::SubConcept(sc) => {
            assert_eq!(sc.name, "Sound");
            assert_eq!(sc.parent_concept, "Animal");
            match &sc.kind {
                | SubConceptKind::Enum { variants } => {
                    assert_eq!(variants, &["Bark"]);
                },
                | _ => panic!("Expected Enum sub-concept"),
            }
        },
        | _ => panic!("Expected SubConcept declaration"),
    }
}

#[test]
fn test_sub_concept_record_single_field() {
    let input = "sub_concept Drink.Temperature { celsius: 20 }";
    let file = parse(input);

    assert_eq!(file.declarations.len(), 1);
    match &file.declarations[0] {
        | Declaration::SubConcept(sc) => {
            assert_eq!(sc.name, "Temperature");
            assert_eq!(sc.parent_concept, "Drink");
            match &sc.kind {
                | SubConceptKind::Record { fields } => {
                    assert_eq!(fields.len(), 1);
                    assert_eq!(fields[0].name, "celsius");
                    assert_eq!(fields[0].value, Value::Number(20));
                },
                | _ => panic!("Expected Record sub-concept"),
            }
        },
        | _ => panic!("Expected SubConcept declaration"),
    }
}

#[test]
fn test_sub_concept_with_trailing_comma() {
    let input = "sub_concept Cup.Size { height: 10, width: 5, }";
    let file = parse(input);

    assert_eq!(file.declarations.len(), 1);
    match &file.declarations[0] {
        | Declaration::SubConcept(sc) => {
            assert_eq!(sc.name, "Size");
            assert_eq!(sc.parent_concept, "Cup");
            match &sc.kind {
                | SubConceptKind::Record { fields } => {
                    assert_eq!(fields.len(), 2);
                },
                | _ => panic!("Expected Record sub-concept"),
            }
        },
        | _ => panic!("Expected SubConcept declaration"),
    }
}

#[test]
fn test_multiple_sub_concepts_same_parent() {
    let input = r#"
sub_concept Cup.Type { Mug, Teacup, Goblet }
sub_concept Cup.Material { Glass, Ceramic, Metal }
"#;
    let file = parse(input);

    assert_eq!(file.declarations.len(), 2);

    match &file.declarations[0] {
        | Declaration::SubConcept(sc) => {
            assert_eq!(sc.parent_concept, "Cup");
            assert_eq!(sc.name, "Type");
        },
        | _ => panic!("Expected SubConcept"),
    }

    match &file.declarations[1] {
        | Declaration::SubConcept(sc) => {
            assert_eq!(sc.parent_concept, "Cup");
            assert_eq!(sc.name, "Material");
        },
        | _ => panic!("Expected SubConcept"),
    }
}

#[test]
fn test_concept_with_sub_concepts() {
    let input = r#"
concept Cup
sub_concept Cup.Type { Small, Medium, Large }
sub_concept Cup.Material { Glass, Ceramic, Metal }
"#;
    let file = parse(input);

    assert_eq!(file.declarations.len(), 3);

    match &file.declarations[0] {
        | Declaration::Concept(c) => assert_eq!(c.name, "Cup"),
        | _ => panic!("Expected Concept"),
    }

    match &file.declarations[1] {
        | Declaration::SubConcept(sc) => {
            assert_eq!(sc.parent_concept, "Cup");
            assert_eq!(sc.name, "Type");
        },
        | _ => panic!("Expected SubConcept"),
    }

    match &file.declarations[2] {
        | Declaration::SubConcept(sc) => {
            assert_eq!(sc.parent_concept, "Cup");
            assert_eq!(sc.name, "Material");
        },
        | _ => panic!("Expected SubConcept"),
    }
}

// ===== Template species base tests =====

#[test]
fn test_template_with_species_base() {
    let input = r#"
template Person: Human {
    age: 30
    name: "default"
}
"#;
    let file = parse(input);

    assert_eq!(file.declarations.len(), 1);
    match &file.declarations[0] {
        | Declaration::Template(t) => {
            assert_eq!(t.name, "Person");
            assert_eq!(t.species_base, Some("Human".to_string()));
            assert_eq!(t.fields.len(), 2);
        },
        | _ => panic!("Expected Template declaration"),
    }
}

#[test]
fn test_template_without_species_base() {
    let input = r#"
template Person {
    age: 30
}
"#;
    let file = parse(input);

    assert_eq!(file.declarations.len(), 1);
    match &file.declarations[0] {
        | Declaration::Template(t) => {
            assert_eq!(t.name, "Person");
            assert_eq!(t.species_base, None);
            assert_eq!(t.fields.len(), 1);
        },
        | _ => panic!("Expected Template declaration"),
    }
}

#[test]
fn test_template_strict_with_species_base() {
    let input = r#"
template Warrior: Human strict {
    strength: 50..100
    weapon: "sword"
}
"#;
    let file = parse(input);

    assert_eq!(file.declarations.len(), 1);
    match &file.declarations[0] {
        | Declaration::Template(t) => {
            assert_eq!(t.name, "Warrior");
            assert_eq!(t.species_base, Some("Human".to_string()));
            assert!(t.strict);
            assert_eq!(t.fields.len(), 2);
        },
        | _ => panic!("Expected Template declaration"),
    }
}

#[test]
fn test_template_species_base_with_includes() {
    let input = r#"
template Knight: Human {
    include Warrior
    honor: 80
}
"#;
    let file = parse(input);

    assert_eq!(file.declarations.len(), 1);
    match &file.declarations[0] {
        | Declaration::Template(t) => {
            assert_eq!(t.name, "Knight");
            assert_eq!(t.species_base, Some("Human".to_string()));
            assert_eq!(t.includes, vec!["Warrior".to_string()]);
            assert_eq!(t.fields.len(), 1);
            assert_eq!(t.fields[0].name, "honor");
        },
        | _ => panic!("Expected Template declaration"),
    }
}

// ===== Life arc requires clause tests =====

#[test]
fn test_life_arc_with_requires() {
    let input = r#"
life_arc Baker requires { skill_level: Number } {
    state Apprentice {
    }
}
"#;
    let file = parse(input);

    assert_eq!(file.declarations.len(), 1);
    match &file.declarations[0] {
        | Declaration::LifeArc(la) => {
            assert_eq!(la.name, "Baker");
            assert_eq!(la.required_fields.len(), 1);
            assert_eq!(la.required_fields[0].name, "skill_level");
            assert_eq!(la.required_fields[0].type_name, "Number");
            assert_eq!(la.states.len(), 1);
        },
        | _ => panic!("Expected LifeArc declaration"),
    }
}

#[test]
fn test_life_arc_without_requires() {
    let input = r#"
life_arc Growth {
    state Child {
    }
}
"#;
    let file = parse(input);

    assert_eq!(file.declarations.len(), 1);
    match &file.declarations[0] {
        | Declaration::LifeArc(la) => {
            assert_eq!(la.name, "Growth");
            assert!(la.required_fields.is_empty());
        },
        | _ => panic!("Expected LifeArc declaration"),
    }
}

#[test]
fn test_life_arc_multiple_requirements() {
    let input = r#"
life_arc Career requires { skill_level: Number, experience: Number, title: Text } {
    state Junior {
    }
}
"#;
    let file = parse(input);

    assert_eq!(file.declarations.len(), 1);
    match &file.declarations[0] {
        | Declaration::LifeArc(la) => {
            assert_eq!(la.required_fields.len(), 3);
            assert_eq!(la.required_fields[0].name, "skill_level");
            assert_eq!(la.required_fields[1].name, "experience");
            assert_eq!(la.required_fields[2].name, "title");
        },
        | _ => panic!("Expected LifeArc declaration"),
    }
}
