// Test various orders
// https://cuelang.org/issues/2235

-- reductions.cue --
// Each of these tests triggers a slightly different path.

t1: {
	params: x.y
	x: y: {}
	for _ in x {
		if true {
			x: y: a: {}
		}
	}
}

t2: {
	out: X.y
	let X = m.x
	m: x: y: {}
	for v in m {
		if true {
			m: x: y: z: {}
		}
	}
}

t3: {
	out: foo.bar

	foo: bar: {}

	for _ in foo {
		for name, _ in foo {
			foo: (name): _
		}
	}
}

-- issue2235.cue --
package kubecluster

import (
	"strings"
	"net"
)

shorewallParams: close({
	[=~"^[A-Z0-9_]+$"]: [...net.IP]
})

#Cluster: {
	clusterName:     string
	shorewallPrefix: "KUBERNETES_\(strings.ToUpper(clusterName))"
}

clusters: [CLUSTER=string]: #Cluster & {
	clusterName: CLUSTER
}

#Cluster: {
	globalIngressControllers: [string]: #GlobalIngressController
	globalIngressControllers: admin:    #GlobalIngressController & {
		class: "admin"
	}
	for _, c in globalIngressControllers {
		objects: c.objects
	}
}

for clusterName, cluster in clusters {
	for class, controller in cluster.globalIngressControllers {
		let svc = controller.objects.namespaced.ingress.Service["\(class)-nginx-ingress-controller"]

		shorewallParams: "\(cluster.shorewallPrefix)_INGRESS_\(strings.ToUpper(class))": svc.spec.externalIPs
	}
}

#GlobalIngressController: {
	class: string

	objects: namespaced: ingress: {
		Service: "\(class)-nginx-ingress-controller": {
			spec: {
				externalIPs: ["127.0.0.1"]
			}
		}
	}

	for kind, objs in objects.namespaced.ingress {
		for name, obj in objs {
			objects: namespaced: ingress: "\(kind)": "\(name)": metadata: labels: {
				"ingress-class": class
				stack:           "ingress"
				...
			}
		}
	}
}

clusters: foo: {}
-- out/eval/stats --
Leaks:  20
Freed:  84
Reused: 79
Allocs: 25
Retain: 68

Unifications: 104
Conjuncts:    271
Disjuncts:    161
-- out/evalalpha --
(struct){
  shorewallParams: (#struct){
    KUBERNETES_FOO_INGRESS_ADMIN: (#list){
      0: (string){ "127.0.0.1" }
    }
  }
  #Cluster: (#struct){
    clusterName: (string){ string }
    shorewallPrefix: (_|_){
      // [incomplete] #Cluster.shorewallPrefix: invalid interpolation: error in call to strings.ToUpper: non-concrete value string:
      //     ./issue2235.cue:14:19
      //     ./issue2235.cue:13:19
      //     ./issue2235.cue:14:33
    }
    globalIngressControllers: (#struct){
      admin: (#struct){
        class: (string){ "admin" }
        objects: (#struct){
          namespaced: (#struct){
            ingress: (#struct){
              Service: (#struct){
                "admin-nginx-ingress-controller": (#struct){
                  spec: (#struct){
                    externalIPs: (#list){
                      0: (string){ "127.0.0.1" }
                    }
                  }
                  metadata: (#struct){
                    labels: (#struct){
                      "ingress-class": (string){ "admin" }
                      stack: (string){ "ingress" }
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
    objects: ~(#Cluster.globalIngressControllers.admin.objects)
  }
  clusters: (struct){
    foo: (#struct){
      clusterName: (string){ "foo" }
      shorewallPrefix: (string){ "KUBERNETES_FOO" }
      globalIngressControllers: (#struct){
        admin: (#struct){
          class: (string){ "admin" }
          objects: (#struct){
            namespaced: (#struct){
              ingress: (#struct){
                Service: (#struct){
                  "admin-nginx-ingress-controller": (#struct){
                    spec: (#struct){
                      externalIPs: (#list){
                        0: (string){ "127.0.0.1" }
                      }
                    }
                    metadata: (#struct){
                      labels: (#struct){
                        "ingress-class": (string){ "admin" }
                        stack: (string){ "ingress" }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
      objects: ~(clusters.foo.globalIngressControllers.admin.objects)
    }
  }
  #GlobalIngressController: (_|_){
    // [incomplete] #GlobalIngressController.objects.namespaced.ingress.Service: key value of dynamic field must be concrete, found _|_(invalid interpolation: #GlobalIngressController.objects.namespaced.ingress.Service: non-concrete value string (type string)):
    //     ./issue2235.cue:43:12
    class: (string){ string }
    objects: (#struct){
      namespaced: (#struct){
        ingress: (_|_){
          // [incomplete] #GlobalIngressController.objects.namespaced.ingress.Service: key value of dynamic field must be concrete, found _|_(invalid interpolation: #GlobalIngressController.objects.namespaced.ingress.Service: non-concrete value string (type string)):
          //     ./issue2235.cue:43:12
          Service: (_|_){
            // [incomplete] #GlobalIngressController.objects.namespaced.ingress.Service: key value of dynamic field must be concrete, found _|_(invalid interpolation: #GlobalIngressController.objects.namespaced.ingress.Service: non-concrete value string (type string)):
            //     ./issue2235.cue:43:12
          }
        }
      }
    }
  }
  t1: (struct){
    params: ~(t1.x.y)
    x: (struct){
      y: (struct){
        a: (struct){
        }
      }
    }
  }
  t2: (struct){
    out: ~(t2.m.x.y)
    let X#2 = ~(t2.m.x)
    m: (struct){
      x: (struct){
        y: (struct){
          z: (struct){
          }
        }
      }
    }
  }
  t3: (struct){
    out: (struct){
    }
    foo: (struct){
      bar: (struct){
      }
    }
  }
  let svc#1multi = 〈1;controller〉.objects.namespaced.ingress.Service["\(〈1;class〉)-nginx-ingress-controller"]
}
-- diff/-out/evalalpha<==>+out/eval --
diff old new
--- old
+++ new
@@ -38,27 +38,7 @@
         }
       }
     }
-    objects: (#struct){
-      namespaced: (#struct){
-        ingress: (#struct){
-          Service: (#struct){
-            "admin-nginx-ingress-controller": (#struct){
-              spec: (#struct){
-                externalIPs: (#list){
-                  0: (string){ "127.0.0.1" }
-                }
-              }
-              metadata: (#struct){
-                labels: (#struct){
-                  "ingress-class": (string){ "admin" }
-                  stack: (string){ "ingress" }
-                }
-              }
-            }
-          }
-        }
-      }
-    }
+    objects: ~(#Cluster.globalIngressControllers.admin.objects)
   }
   clusters: (struct){
     foo: (#struct){
@@ -90,44 +70,21 @@
           }
         }
       }
-      objects: (#struct){
-        namespaced: (#struct){
-          ingress: (#struct){
-            Service: (#struct){
-              "admin-nginx-ingress-controller": (#struct){
-                spec: (#struct){
-                  externalIPs: (#list){
-                    0: (string){ "127.0.0.1" }
-                  }
-                }
-                metadata: (#struct){
-                  labels: (#struct){
-                    "ingress-class": (string){ "admin" }
-                    stack: (string){ "ingress" }
-                  }
-                }
-              }
-            }
-          }
-        }
-      }
+      objects: ~(clusters.foo.globalIngressControllers.admin.objects)
     }
   }
   #GlobalIngressController: (_|_){
-    // [incomplete] #GlobalIngressController.objects.namespaced.ingress.Service: invalid interpolation: non-concrete value string (type string):
+    // [incomplete] #GlobalIngressController.objects.namespaced.ingress.Service: key value of dynamic field must be concrete, found _|_(invalid interpolation: #GlobalIngressController.objects.namespaced.ingress.Service: non-concrete value string (type string)):
     //     ./issue2235.cue:43:12
-    //     ./issue2235.cue:40:9
     class: (string){ string }
     objects: (#struct){
       namespaced: (#struct){
         ingress: (_|_){
-          // [incomplete] #GlobalIngressController.objects.namespaced.ingress.Service: invalid interpolation: non-concrete value string (type string):
+          // [incomplete] #GlobalIngressController.objects.namespaced.ingress.Service: key value of dynamic field must be concrete, found _|_(invalid interpolation: #GlobalIngressController.objects.namespaced.ingress.Service: non-concrete value string (type string)):
           //     ./issue2235.cue:43:12
-          //     ./issue2235.cue:40:9
           Service: (_|_){
-            // [incomplete] #GlobalIngressController.objects.namespaced.ingress.Service: invalid interpolation: non-concrete value string (type string):
+            // [incomplete] #GlobalIngressController.objects.namespaced.ingress.Service: key value of dynamic field must be concrete, found _|_(invalid interpolation: #GlobalIngressController.objects.namespaced.ingress.Service: non-concrete value string (type string)):
             //     ./issue2235.cue:43:12
-            //     ./issue2235.cue:40:9
           }
         }
       }
@@ -134,10 +91,7 @@
     }
   }
   t1: (struct){
-    params: (struct){
-      a: (struct){
-      }
-    }
+    params: ~(t1.x.y)
     x: (struct){
       y: (struct){
         a: (struct){
@@ -146,16 +100,8 @@
     }
   }
   t2: (struct){
-    out: (struct){
-      z: (struct){
-      }
-    }
-    let X#2 = (struct){
-      y: (struct){
-        z: (struct){
-        }
-      }
-    }
+    out: ~(t2.m.x.y)
+    let X#2 = ~(t2.m.x)
     m: (struct){
       x: (struct){
         y: (struct){
-- diff/todo/p2 --
Different, but okay, error message, but missing some positions
-- out/eval --
(struct){
  shorewallParams: (#struct){
    KUBERNETES_FOO_INGRESS_ADMIN: (#list){
      0: (string){ "127.0.0.1" }
    }
  }
  #Cluster: (#struct){
    clusterName: (string){ string }
    shorewallPrefix: (_|_){
      // [incomplete] #Cluster.shorewallPrefix: invalid interpolation: error in call to strings.ToUpper: non-concrete value string:
      //     ./issue2235.cue:14:19
      //     ./issue2235.cue:13:19
      //     ./issue2235.cue:14:33
    }
    globalIngressControllers: (#struct){
      admin: (#struct){
        class: (string){ "admin" }
        objects: (#struct){
          namespaced: (#struct){
            ingress: (#struct){
              Service: (#struct){
                "admin-nginx-ingress-controller": (#struct){
                  spec: (#struct){
                    externalIPs: (#list){
                      0: (string){ "127.0.0.1" }
                    }
                  }
                  metadata: (#struct){
                    labels: (#struct){
                      "ingress-class": (string){ "admin" }
                      stack: (string){ "ingress" }
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
    objects: (#struct){
      namespaced: (#struct){
        ingress: (#struct){
          Service: (#struct){
            "admin-nginx-ingress-controller": (#struct){
              spec: (#struct){
                externalIPs: (#list){
                  0: (string){ "127.0.0.1" }
                }
              }
              metadata: (#struct){
                labels: (#struct){
                  "ingress-class": (string){ "admin" }
                  stack: (string){ "ingress" }
                }
              }
            }
          }
        }
      }
    }
  }
  clusters: (struct){
    foo: (#struct){
      clusterName: (string){ "foo" }
      shorewallPrefix: (string){ "KUBERNETES_FOO" }
      globalIngressControllers: (#struct){
        admin: (#struct){
          class: (string){ "admin" }
          objects: (#struct){
            namespaced: (#struct){
              ingress: (#struct){
                Service: (#struct){
                  "admin-nginx-ingress-controller": (#struct){
                    spec: (#struct){
                      externalIPs: (#list){
                        0: (string){ "127.0.0.1" }
                      }
                    }
                    metadata: (#struct){
                      labels: (#struct){
                        "ingress-class": (string){ "admin" }
                        stack: (string){ "ingress" }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
      objects: (#struct){
        namespaced: (#struct){
          ingress: (#struct){
            Service: (#struct){
              "admin-nginx-ingress-controller": (#struct){
                spec: (#struct){
                  externalIPs: (#list){
                    0: (string){ "127.0.0.1" }
                  }
                }
                metadata: (#struct){
                  labels: (#struct){
                    "ingress-class": (string){ "admin" }
                    stack: (string){ "ingress" }
                  }
                }
              }
            }
          }
        }
      }
    }
  }
  #GlobalIngressController: (_|_){
    // [incomplete] #GlobalIngressController.objects.namespaced.ingress.Service: invalid interpolation: non-concrete value string (type string):
    //     ./issue2235.cue:43:12
    //     ./issue2235.cue:40:9
    class: (string){ string }
    objects: (#struct){
      namespaced: (#struct){
        ingress: (_|_){
          // [incomplete] #GlobalIngressController.objects.namespaced.ingress.Service: invalid interpolation: non-concrete value string (type string):
          //     ./issue2235.cue:43:12
          //     ./issue2235.cue:40:9
          Service: (_|_){
            // [incomplete] #GlobalIngressController.objects.namespaced.ingress.Service: invalid interpolation: non-concrete value string (type string):
            //     ./issue2235.cue:43:12
            //     ./issue2235.cue:40:9
          }
        }
      }
    }
  }
  t1: (struct){
    params: (struct){
      a: (struct){
      }
    }
    x: (struct){
      y: (struct){
        a: (struct){
        }
      }
    }
  }
  t2: (struct){
    out: (struct){
      z: (struct){
      }
    }
    let X#2 = (struct){
      y: (struct){
        z: (struct){
        }
      }
    }
    m: (struct){
      x: (struct){
        y: (struct){
          z: (struct){
          }
        }
      }
    }
  }
  t3: (struct){
    out: (struct){
    }
    foo: (struct){
      bar: (struct){
      }
    }
  }
  let svc#1multi = 〈1;controller〉.objects.namespaced.ingress.Service["\(〈1;class〉)-nginx-ingress-controller"]
}
-- out/compile --
--- issue2235.cue
{
  shorewallParams: close({
    [=~"^[A-Z0-9_]+$"]: [
      ...〈import;net〉.IP,
    ]
  })
  #Cluster: {
    clusterName: string
    shorewallPrefix: "KUBERNETES_\(〈import;strings〉.ToUpper(〈0;clusterName〉))"
  }
  clusters: {
    [string]: (〈1;#Cluster〉 & {
      clusterName: 〈1;-〉
    })
  }
  #Cluster: {
    globalIngressControllers: {
      [string]: 〈2;#GlobalIngressController〉
    }
    globalIngressControllers: {
      admin: (〈2;#GlobalIngressController〉 & {
        class: "admin"
      })
    }
    for _, c in 〈0;globalIngressControllers〉 {
      objects: 〈1;c〉.objects
    }
  }
  for clusterName, cluster in 〈0;clusters〉 {
    for class, controller in 〈1;cluster〉.globalIngressControllers {
      let svc#1multi = 〈1;controller〉.objects.namespaced.ingress.Service["\(〈1;class〉)-nginx-ingress-controller"]
      shorewallParams: {
        "\(〈4;cluster〉.shorewallPrefix)_INGRESS_\(〈import;strings〉.ToUpper(〈2;class〉))": 〈1;let svc#1〉.spec.externalIPs
      }
    }
  }
  #GlobalIngressController: {
    class: string
    objects: {
      namespaced: {
        ingress: {
          Service: {
            "\(〈4;class〉)-nginx-ingress-controller": {
              spec: {
                externalIPs: [
                  "127.0.0.1",
                ]
              }
            }
          }
        }
      }
    }
    for kind, objs in 〈0;objects〉.namespaced.ingress {
      for name, obj in 〈1;objs〉 {
        objects: {
          namespaced: {
            ingress: {
              "\(〈6;kind〉)": {
                "\(〈5;name〉)": {
                  metadata: {
                    labels: {
                      "ingress-class": 〈11;class〉
                      stack: "ingress"
                      ...
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  }
  clusters: {
    foo: {}
  }
}
--- reductions.cue
{
  t1: {
    params: 〈0;x〉.y
    x: {
      y: {}
    }
    for _, _ in 〈0;x〉 {
      if true {
        x: {
          y: {
            a: {}
          }
        }
      }
    }
  }
  t2: {
    out: 〈0;let X#2〉.y
    let X#2 = 〈0;m〉.x
    m: {
      x: {
        y: {}
      }
    }
    for _, v in 〈0;m〉 {
      if true {
        m: {
          x: {
            y: {
              z: {}
            }
          }
        }
      }
    }
  }
  t3: {
    out: 〈0;foo〉.bar
    foo: {
      bar: {}
    }
    for _, _ in 〈0;foo〉 {
      for name, _ in 〈2;foo〉 {
        foo: {
          〈2;name〉: _
        }
      }
    }
  }
}
