use lexicon_spec::contract::Contract;

/// Generate a trait-based conformance harness.
pub fn trait_based_harness(contract: &Contract) -> String {
    let module_name = contract.id.replace('-', "_");
    let trait_name = to_pascal_case(&contract.id);

    let mut code = String::new();

    code.push_str(&format!(
        "//! Conformance harness for contract: {}\n",
        contract.title
    ));
    code.push_str(&format!("//! Contract ID: {}\n", contract.id));
    code.push_str("//!\n");
    code.push_str("//! This file is generated by lexicon. Do not edit the trait definition.\n");
    code.push_str("//! Implement the trait for your type and run the conformance tests.\n\n");

    // Trait definition
    code.push_str(&format!("pub trait {trait_name}Conformance {{\n"));
    code.push_str("    type Instance;\n\n");
    code.push_str("    fn create_instance() -> Self::Instance;\n");
    code.push_str("}\n\n");

    // Test module
    code.push_str("#[cfg(test)]\n");
    code.push_str(&format!("mod {module_name}_conformance {{\n"));
    code.push_str("    use super::*;\n\n");

    // Generate test for each invariant
    for inv in &contract.invariants {
        let test_name = inv.id.replace('-', "_");
        code.push_str(&format!("    /// Invariant: {}\n", inv.description));
        code.push_str(&format!("    #[test]\n"));
        code.push_str(&format!("    fn {test_name}() {{\n"));
        code.push_str(&format!(
            "        // TODO: Implement test for invariant: {}\n",
            inv.description
        ));
        code.push_str("        // let instance = T::create_instance();\n");
        code.push_str("    }\n\n");
    }

    // Generate test for each required semantic
    for sem in &contract.required_semantics {
        let test_name = sem.id.replace('-', "_");
        code.push_str(&format!("    /// Required: {}\n", sem.description));
        code.push_str(&format!("    #[test]\n"));
        code.push_str(&format!("    fn {test_name}() {{\n"));
        code.push_str(&format!(
            "        // TODO: Implement test for: {}\n",
            sem.description
        ));
        code.push_str("    }\n\n");
    }

    // Generate test for each forbidden semantic
    for sem in &contract.forbidden_semantics {
        let test_name = sem.id.replace('-', "_");
        code.push_str(&format!(
            "    /// Forbidden: {} — this test should verify the behavior does NOT occur\n",
            sem.description
        ));
        code.push_str(&format!("    #[test]\n"));
        code.push_str(&format!("    fn {test_name}() {{\n"));
        code.push_str(&format!(
            "        // TODO: Verify forbidden behavior does not occur: {}\n",
            sem.description
        ));
        code.push_str("    }\n\n");
    }

    code.push_str("}\n");
    code
}

/// Generate a factory-based conformance harness.
pub fn factory_based_harness(contract: &Contract) -> String {
    let module_name = contract.id.replace('-', "_");

    let mut code = String::new();

    code.push_str(&format!(
        "//! Factory-based conformance harness for contract: {}\n",
        contract.title
    ));
    code.push_str(&format!("//! Contract ID: {}\n", contract.id));
    code.push_str("//!\n");
    code.push_str("//! This file is generated by lexicon. Implement `create_instance()`.\n\n");

    // Factory function placeholder
    code.push_str("/// Create an instance of the implementation under test.\n");
    code.push_str("///\n");
    code.push_str("/// Replace the return type and body with your actual implementation.\n");
    code.push_str("fn create_instance() -> () {\n");
    code.push_str("    // TODO: Return your implementation instance\n");
    code.push_str("    ()\n");
    code.push_str("}\n\n");

    // Test module
    code.push_str("#[cfg(test)]\n");
    code.push_str(&format!("mod {module_name}_conformance {{\n"));
    code.push_str("    use super::*;\n\n");

    for inv in &contract.invariants {
        let test_name = inv.id.replace('-', "_");
        code.push_str(&format!("    /// Invariant: {}\n", inv.description));
        code.push_str("    #[test]\n");
        code.push_str(&format!("    fn {test_name}() {{\n"));
        code.push_str("        let _instance = create_instance();\n");
        code.push_str(&format!(
            "        // TODO: Verify invariant: {}\n",
            inv.description
        ));
        code.push_str("    }\n\n");
    }

    for sem in &contract.required_semantics {
        let test_name = sem.id.replace('-', "_");
        code.push_str(&format!("    /// Required: {}\n", sem.description));
        code.push_str("    #[test]\n");
        code.push_str(&format!("    fn {test_name}() {{\n"));
        code.push_str("        let _instance = create_instance();\n");
        code.push_str(&format!(
            "        // TODO: Verify: {}\n",
            sem.description
        ));
        code.push_str("    }\n\n");
    }

    for sem in &contract.forbidden_semantics {
        let test_name = sem.id.replace('-', "_");
        code.push_str(&format!("    /// Forbidden: {}\n", sem.description));
        code.push_str("    #[test]\n");
        code.push_str(&format!("    fn {test_name}() {{\n"));
        code.push_str("        let _instance = create_instance();\n");
        code.push_str(&format!(
            "        // TODO: Verify does NOT occur: {}\n",
            sem.description
        ));
        code.push_str("    }\n\n");
    }

    code.push_str("}\n");
    code
}

/// Convert kebab-case to PascalCase.
fn to_pascal_case(s: &str) -> String {
    s.split('-')
        .map(|part| {
            let mut chars = part.chars();
            match chars.next() {
                None => String::new(),
                Some(c) => c.to_uppercase().to_string() + &chars.as_str().to_lowercase(),
            }
        })
        .collect()
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_to_pascal_case() {
        assert_eq!(to_pascal_case("key-value-store"), "KeyValueStore");
        assert_eq!(to_pascal_case("parser"), "Parser");
        assert_eq!(to_pascal_case("my-lib"), "MyLib");
    }
}
