// tests/table_definition/post_table_definition_test5.rs

// NOTE: All 'use' statements are inherited from the parent file that includes this one.

// ========= Category 7: Schema Validation and Edge Cases =========

#[rstest]
#[tokio::test]
async fn test_schema_name_validation_reserved_names(#[future] pool: PgPool) {
    let pool = pool.await;
    
    let reserved_names = vec![
        "pg_catalog",
        "information_schema", 
        "pg_toast",
        "public", // May be reserved depending on implementation
    ];
    
    for reserved_name in reserved_names {
        let request = PostTableDefinitionRequest {
            profile_name: reserved_name.into(),
            table_name: "test_table".into(),
            columns: vec![],
            indexes: vec![],
            links: vec![],
        };
        
        let result = post_table_definition(&pool, request).await;
        assert!(result.is_err(), "Reserved schema name '{}' should be rejected", reserved_name);
        if let Err(status) = result {
            assert_eq!(status.code(), Code::InvalidArgument);
        }
    }
}

#[rstest]
#[tokio::test]
async fn test_schema_name_validation_sql_injection(#[future] pool: PgPool) {
    let pool = pool.await;
    
    let malicious_names = vec![
        "test; DROP SCHEMA public",
        "test'; DROP TABLE users; --",
        "test\"; CREATE TABLE evil; --",
    ];
    
    for malicious_name in malicious_names {
        let request = PostTableDefinitionRequest {
            profile_name: malicious_name.into(),
            table_name: "test_table".into(),
            columns: vec![],
            indexes: vec![],
            links: vec![],
        };
        
        let result = post_table_definition(&pool, request).await;
        assert!(result.is_err(), "Malicious schema name '{}' should be rejected", malicious_name);
        if let Err(status) = result {
            assert_eq!(status.code(), Code::InvalidArgument);
            assert!(status.message().contains("contains invalid characters"));
        }
    }
}

#[rstest]
#[tokio::test]
async fn test_schema_name_length_limits(#[future] pool: PgPool) {
    let pool = pool.await;
    
    // Test schema name length limits (63 chars in PostgreSQL)
    let long_name = "a".repeat(64);
    let request = PostTableDefinitionRequest {
        profile_name: long_name,
        table_name: "test_table".into(),
        columns: vec![],
        indexes: vec![],
        links: vec![],
    };
    
    let result = post_table_definition(&pool, request).await;
    assert!(result.is_err(), "Schema names longer than 63 characters should be rejected");
    if let Err(status) = result {
        assert_eq!(status.code(), Code::InvalidArgument);
    }
}

#[rstest]
#[tokio::test]
async fn test_unicode_in_schema_names_rejected(#[future] pool: PgPool) {
    let pool = pool.await;
    
    let unicode_names = vec![
        "test_😀",
        "schéma", 
        "тест",
        "测试",
    ];
    
    for unicode_name in unicode_names {
        let request = PostTableDefinitionRequest {
            profile_name: unicode_name.into(),
            table_name: "test_table".into(),
            columns: vec![],
            indexes: vec![],
            links: vec![],
        };
        
        let result = post_table_definition(&pool, request).await;
        assert!(result.is_err(), "Unicode schema name '{}' should be rejected", unicode_name);
        if let Err(status) = result {
            assert_eq!(status.code(), Code::InvalidArgument);
            assert!(status.message().contains("contains invalid characters"));
        }
    }
}

// ========= Category 8: Foreign Key Edge Cases =========

#[rstest]
#[tokio::test]
async fn test_fk_column_name_uniqueness_collision(#[future] pool: PgPool) {
    let pool = pool.await;

    // Create tables with similar suffixes
    let req1 = PostTableDefinitionRequest {
        profile_name: "default".into(),
        table_name: "customers_146053".into(),
        columns: vec![],
        indexes: vec![],
        links: vec![],
    };
    post_table_definition(&pool, req1).await.unwrap();

    let req2 = PostTableDefinitionRequest {
        profile_name: "default".into(),
        table_name: "suppliers_146053".into(),
        columns: vec![],
        indexes: vec![],
        links: vec![],
    };
    post_table_definition(&pool, req2).await.unwrap();

    // Create a table linking to both - should succeed with full table names
    let request = PostTableDefinitionRequest {
        profile_name: "default".into(),
        table_name: "orders_test".into(), // Use unique name to avoid conflicts
        columns: vec![],
        indexes: vec![],
        links: vec![
            TableLink {
                linked_table_name: "customers_146053".into(),
                required: true,
            },
            TableLink {
                linked_table_name: "suppliers_146053".into(),
                required: true,
            },
        ],
    };

    let result = post_table_definition(&pool, request).await;

    // Should succeed - no collision with full table names
    assert!(result.is_ok());
    let response = result.unwrap();

    // Verify both FK columns are created with full table names
    assert!(response.sql.contains("\"customers_146053_id\""));
    assert!(response.sql.contains("\"suppliers_146053_id\""));

    // Verify both are NOT NULL (required = true)
    assert!(response.sql.contains("\"customers_146053_id\" BIGINT NOT NULL"));
    assert!(response.sql.contains("\"suppliers_146053_id\" BIGINT NOT NULL"));
}

#[rstest]
#[tokio::test]
async fn test_cross_schema_references_prevented(#[future] pool: PgPool) {
    let pool = pool.await;
    
    // Create table in schema A
    let req_a = PostTableDefinitionRequest {
        profile_name: "scheam_a".into(),
        table_name: "users".into(),
        columns: vec![],
        indexes: vec![],
        links: vec![],
    };
    post_table_definition(&pool, req_a).await.unwrap();
    
    // Try to link from schema B to schema A's table
    let req_b = PostTableDefinitionRequest {
        profile_name: "schema_b".into(),
        table_name: "orders".into(),
        columns: vec![],
        indexes: vec![],
        links: vec![TableLink {
            linked_table_name: "users".into(), // This should not find A.users
            required: true,
        }],
    };
    
    let result = post_table_definition(&pool, req_b).await;
    assert!(result.is_err());
    assert!(result.unwrap_err().message().contains("not found"));
}

// ========= Category 9: Concurrent Operations =========

#[rstest]
#[tokio::test]
async fn test_concurrent_schema_creation(#[future] pool: PgPool) {
    let pool = pool.await;
    
    use futures::future::join_all;
    
    let futures = (0..10).map(|i| {
        let pool = pool.clone();
        async move {
            let request = PostTableDefinitionRequest {
                profile_name: format!("concurrent_schema_{}", i),
                table_name: "test_table".into(),
                columns: vec![ColumnDefinition {
                    name: "test_column".into(),
                    field_type: "text".into(),
                }],
                indexes: vec![],
                links: vec![],
            };
            post_table_definition(&pool, request).await
        }
    });
    
    let results = join_all(futures).await;
    assert!(results.iter().all(|r| r.is_ok()));
}

#[rstest]
#[tokio::test]
async fn test_table_creation_with_many_foreign_keys(#[future] pool: PgPool) {
    let pool = pool.await;
    
    // Create several tables to link to
    for i in 0..5 {
        let request = PostTableDefinitionRequest {
            profile_name: "default".into(),
            table_name: format!("target_table_{}", i),
            columns: vec![],
            indexes: vec![],
            links: vec![],
        };
        post_table_definition(&pool, request).await.unwrap();
    }
    
    // Create a table that links to all of them
    let links = (0..5).map(|i| TableLink {
        linked_table_name: format!("target_table_{}", i),
        required: false,
    }).collect();
    
    let request = PostTableDefinitionRequest {
        profile_name: "default".into(),
        table_name: "many_links_table".into(),
        columns: vec![],
        indexes: vec![],
        links,
    };
    
    let result = post_table_definition(&pool, request).await;
    assert!(result.is_ok());
}

// ========= Category 10: Empty and Boundary Cases =========

#[rstest]
#[tokio::test]
async fn test_empty_schema_and_table_names_rejected(#[future] pool: PgPool) {
    let pool = pool.await;
    
    // Test empty schema name
    let request = PostTableDefinitionRequest {
        profile_name: "".into(),
        table_name: "valid_table".into(),
        columns: vec![],
        indexes: vec![],
        links: vec![],
    };
    let result = post_table_definition(&pool, request).await;
    assert!(result.is_err());
    assert_eq!(result.unwrap_err().code(), Code::InvalidArgument);
    
    // Test empty table name
    let request = PostTableDefinitionRequest {
        profile_name: "default".into(),
        table_name: "".into(),
        columns: vec![],
        indexes: vec![],
        links: vec![],
    };
    let result = post_table_definition(&pool, request).await;
    assert!(result.is_err());
    assert_eq!(result.unwrap_err().code(), Code::InvalidArgument);
}

#[rstest]
#[tokio::test]
async fn test_schema_name_case_sensitivity(#[future] pool: PgPool) {
    let pool = pool.await;

    // First, verify that uppercase letters are rejected
    let invalid_request = PostTableDefinitionRequest {
        profile_name: "TestSchema".into(), // Contains uppercase - should be rejected
        table_name: "test_table".into(),
        columns: vec![],
        indexes: vec![],
        links: vec![],
    };
    let result = post_table_definition(&pool, invalid_request).await;
    assert!(result.is_err());
    let err = result.unwrap_err();
    assert_eq!(err.code(), Code::InvalidArgument);
    assert!(err.message().contains("contains invalid characters"));

    // Now test with valid lowercase names - create first schema
    let request1 = PostTableDefinitionRequest {
        profile_name: "test_schema_a".into(),
        table_name: "test_table".into(),
        columns: vec![],
        indexes: vec![],
        links: vec![],
    };
    post_table_definition(&pool, request1).await.unwrap();

    // Different lowercase schema should work fine
    let request2 = PostTableDefinitionRequest {
        profile_name: "test_schema_b".into(),
        table_name: "test_table".into(), // Same table name, different schema
        columns: vec![],
        indexes: vec![],
        links: vec![],
    };
    let result2 = post_table_definition(&pool, request2).await;
    assert!(result2.is_ok(), "Different schemas should allow same table names");

    // Same schema name should cause table collision
    let request3 = PostTableDefinitionRequest {
        profile_name: "test_schema_a".into(), // Same schema as request1
        table_name: "test_table".into(),      // Same table name as request1
        columns: vec![],
        indexes: vec![],
        links: vec![],
    };
    let result3 = post_table_definition(&pool, request3).await;
    assert!(result3.is_err(), "Same schema + table should cause collision");
    let err3 = result3.unwrap_err();
    assert_eq!(err3.code(), Code::AlreadyExists);
}

#[rstest]
#[tokio::test]
async fn test_whitespace_in_identifiers_rejected(#[future] pool: PgPool) {
    let pool = pool.await;
    
    // Test schema name with whitespace
    let request = PostTableDefinitionRequest {
        profile_name: "test schema".into(),
        table_name: "test_table".into(),
        columns: vec![],
        indexes: vec![],
        links: vec![],
    };
    let result = post_table_definition(&pool, request).await;
    assert!(result.is_err());
    assert_eq!(result.unwrap_err().code(), Code::InvalidArgument);
    
    // Test table name with whitespace
    let request = PostTableDefinitionRequest {
        profile_name: "default".into(),
        table_name: "test table".into(),
        columns: vec![],
        indexes: vec![],
        links: vec![],
    };
    let result = post_table_definition(&pool, request).await;
    assert!(result.is_err());
    assert_eq!(result.unwrap_err().code(), Code::InvalidArgument);
    
    // Test column name with whitespace
    let request = PostTableDefinitionRequest {
        profile_name: "default".into(),
        table_name: "test_table".into(),
        columns: vec![ColumnDefinition {
            name: "test column".into(),
            field_type: "text".into(),
        }],
        indexes: vec![],
        links: vec![],
    };
    let result = post_table_definition(&pool, request).await;
    assert!(result.is_err());
    assert_eq!(result.unwrap_err().code(), Code::InvalidArgument);
}
