The problem is in your MATCH patterns.
Let's first look at why p3 isn't showing up as a result.
Here's the first line of your query:
MATCH (C2:Company)-[:DEVELOPES]->(p2)-[:NEEDED_IN]->(p1)<-[:DEVELOPES]-(C1:Company{name : 'C1'})
Only your p1 node fits the pattern and is bound to the p1 variable, since C1 develops it, and it has incoming relationships of type :NEEDED_IN from some node p2 developed by a company.
At this point, only nodes p3 and p2 will match to this pattern for the variable p2.
However your next MATCH adds a restriction on this:
MATCH (C3:Company)-[:DEVELOPES]->(p3)-[:NEEDED_IN]->(p2)
This means that the node for variable p2 must have an incoming :NEEDED_IN relationship from some node developed by a Company.
Node p3 in your graph does not have any incoming :NEEDED_IN relationships, so it is filtered out. This is why p3 is not in your graph, it doesn't fit the pattern you specified.
Node p2 however does fit the pattern, as it has incoming :NEEDED_IN relationships from nodes p4 (developed by C3), p5 and p6 (both developed by C1). You didn't have any other restrictions on variable C3 besides that it's a :Company node, so there are no problems with it matching to node C1 in your graph.
Thus your final graph:
Company C3 provides product p4 to company C2 (level 2 for product p2)
Company C1 provides products p5 and p6 to C2 (level 2 for product p2)
Company C2 provides product p2 to to C1 (level 1)
Why does p2 show up in the products list twice, and why are there two relationships for p2? We can see why if we look at your results mid-query after your matches:
MATCH (C2:Company)-[:DEVELOPES]->(p2)-[:NEEDED_IN]->(p1)<-[:DEVELOPES]-(C1:Company{name : 'C1'})
MATCH (C3:Company)-[:DEVELOPES]->(p3)-[:NEEDED_IN]->(p2)
RETURN C1, C2, C3, p2.PRODUCT_NAME, p3.PRODUCT_NAME
โโโโโโโโโโโโโโโคโโโโโโโโโโโโโโคโโโโโโโโโโโโโโคโโโโโโโโโโโโโโโโโโคโโโโโโโโโโโโโโโโโโ
โ"C1" โ"C2" โ"C3" โ"p2.PRODUCT_NAME"โ"p3.PRODUCT_NAME"โ
โโโโโโโโโโโโโโโชโโโโโโโโโโโโโโชโโโโโโโโโโโโโโชโโโโโโโโโโโโโโโโโโชโโโโโโโโโโโโโโโโโโก
โ{"name":"C1"}โ{"name":"C2"}โ{"name":"C1"}โ"p2" โ"p6" โ
โโโโโโโโโโโโโโโผโโโโโโโโโโโโโโผโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโค
โ{"name":"C1"}โ{"name":"C2"}โ{"name":"C1"}โ"p2" โ"p5" โ
โโโโโโโโโโโโโโโผโโโโโโโโโโโโโโผโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโค
โ{"name":"C1"}โ{"name":"C2"}โ{"name":"C3"}โ"p2" โ"p4" โ
โโโโโโโโโโโโโโโดโโโโโโโโโโโโโโดโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโ
When you aggregate as in your collect() usage, the non-aggregation variables become the grouping key. You're doing a lot at once here so it's harder for you to see what's actually going on. Here's a simplification, leaving out the relationship creation:
MATCH (C2:Company)-[:DEVELOPES]->(p2)-[:NEEDED_IN]->(p1)<-[:DEVELOPES]-(C1:Company{name : 'C1'})
MATCH (C3:Company)-[:DEVELOPES]->(p3)-[:NEEDED_IN]->(p2)
RETURN C1, C2, C3, collect(p2.PRODUCT_NAME) as p2Products, collect(p3.PRODUCT_NAME) as p3Products
โโโโโโโโโโโโโโโคโโโโโโโโโโโโโโคโโโโโโโโโโโโโโคโโโโโโโโโโโโโคโโโโโโโโโโโโโ
โ"C1" โ"C2" โ"C3" โ"p2Products"โ"p3Products"โ
โโโโโโโโโโโโโโโชโโโโโโโโโโโโโโชโโโโโโโโโโโโโโชโโโโโโโโโโโโโชโโโโโโโโโโโโโก
โ{"name":"C1"}โ{"name":"C2"}โ{"name":"C1"}โ["p2","p2"] โ["p6","p5"] โ
โโโโโโโโโโโโโโโผโโโโโโโโโโโโโโผโโโโโโโโโโโโโโผโโโโโโโโโโโโโผโโโโโโโโโโโโโค
โ{"name":"C1"}โ{"name":"C2"}โ{"name":"C3"}โ["p2"] โ["p4"] โ
โโโโโโโโโโโโโโโดโโโโโโโโโโโโโโดโโโโโโโโโโโโโโดโโโโโโโโโโโโโดโโโโโโโโโโโโโ
In the first row, there are two p2 entries (in the first relationship from C2 to C1), because of the first two rows of the previous table, where C1 = C1, C2 = C2, and C3 = C1. To deduplicate, you can use COLLECT(DISTINCT p2) instead when you collect (and likewise for p3).
But you still have the second row, which will create a second relationship from C2 to C1 with a single p2 as the list entry.
The main point here is you're trying to do too much at once, the match patterns you're looking for are interfering with each other and messing up your aggregations.