// Tests bulk pattern constraints including pattern cycles, multiple pattern
// constraints, and field-not-allowed errors from pattern constraint closedness.
// Also covers regressions: issue 2109 (pattern cycle detection), issue 3638
// (closedness/default markers erased in multiple contexts), and issue 3670
// (int pattern incorrectly allowing non-int fields).
-- in.cue --
a: {
	foo: a: 1
	[X = =~"foo"]: {
		b:    1
		name: X
	}
}
d: a & {
	"foobar": {
		c: 2
	}
}

t1: {
	#a: {
		[>"e" & <"z"]: int
	}

	// TODO(inline): support allow on fields.
	b: #a & {f: 4}
	c: #a & {z: 4}
}

t2: {
	#a: {
		["x" | "y"]: int
	}

	// TODO(inline): support allow on fields.
	b: #a & {x: 4}
	c: #a & {z: 4}
}
-- issue2109.cue --
// Detect cycle:
// The pattern constraint of FlagsURLS is applied to countries (because
// FlagsURLs  is embedded. But the pattern constraint itself refers to
// countries. So countries -> or(countries) -> countries.
//
// Theoretically, or(countries) can be resolved without a cycle. And then we
// could see it does not apply to countries. For now we stick with this
// approach understanding that it is easier to relax later.
patternCycle: issue2109: p1: {
	// TODO(inline: permute doesn't permute embeddings, so the permute count
	// here is 2, not 6. Fix this.
	countries: ["US", "GB", "AU"]
	FlagsURLs: {[or(countries)]: string}
	FlagsURLs
}
patternCycle: issue2109: p2: {
	FlagsURLs: {[or(countries)]: string}
	FlagsURLs
	countries: ["US", "GB", "AU"]
}
patternCycle: issue2109: p3: {
	FlagsURLs
	FlagsURLs: {[or(countries)]: string}
	countries: ["US", "GB", "AU"]
}

patternCycle: t1: p1: {
	countries: "US" | "GB" | "AU"
	FlagsURLs: {[countries]: string}
	FlagsURLs
}
patternCycle: t1: p2: {
	FlagsURLs
	countries: "US" | "GB" | "AU"
	FlagsURLs: {[countries]: string}
}
patternCycle: t1: p3: {
	FlagsURLs
	FlagsURLs: {[countries]: string}
	countries: "US" | "GB" | "AU"
}
-- intpattern.cue --
// evalv3 regression: a pattern constraint definition with int key incorrectly
// allowed a non-matching (non-int) field.
// https://cuelang.org/issue/3670
issue3670: {
	#X: [int]: string
	a: #X
	a: "not_int": "c"
}
dual: {
	_

	// As long as it is undecided whether this is a struct or list, this should
	// not be a conflict. Note that the spec does not allow int patterns, but
	// both v2 and v3 allow it, and we soon will, so it seems gratuitous to
	// break it now.
	[string]: _
	[int]:    _
}
-- multi.cue --
// evalv3 regression: closedness values and default markers were incorrectly
// erased, causing order-dependent "incomplete value" errors.
// https://cuelang.org/issue/3638
// Ensure that if the same pattern is added multiple times in different
// contexts, it is added to all contexts to preserve closedness information.
issue3638: full: {
	routes: default: {}
	routes: [string]: spec: [{

		refs: [{"default"}]
	}]
	#Fields: {
		input: _
		output: [for x in input {x}]
		...
	}
	#Kubernetes: {
		#Fields & {
			Routes: route: [_]: spec!: HTTPROUTESPEC.#x
		}
		#Fields // Commenting out this line also fixes evalv3.
		Routes: _
		input: res1: Routes
	}
	let HTTPROUTESPEC = {
		#x: [...{
			refs: [...{string}]
		}] | *[{
			other: []
		}]
	}

	// Order dependent! Breaks if the lines below are moved to the top of the file.
	entrypoint: used.output
	used: #Kubernetes & {
		Routes: route: routes
	}
}
issue3638: reduced: {
	input: k1: [string]: a: 1
	input: k1: foo: #x
	out: {
		for x in input {x}
		for x in input {x}
	}
	#x: {a: int} | *{b: 2}
}
// We expect that changing the pattern constraint into a field should give
// not materially change the outcome.
issue3638: compare: {
	input: k1: foo: a: 1
	input: k1: foo: #x
	out: {
		for x in input {x}
		for x in input {x}
	}
	#x: {a: int} | *{b: 2}
}
-- out/compile --
--- in.cue
{
  a: {
    foo: {
      a: 1
    }
    [=~"foo"]: {
      b: 1
      name: 〈1;-〉
    }
  }
  d: (〈0;a〉 & {
    foobar: {
      c: 2
    }
  })
  t1: {
    #a: {
      [(>"e" & <"z")]: int
    }
    b: (〈0;#a〉 & {
      f: 4
    })
    c: (〈0;#a〉 & {
      z: 4
    })
  }
  t2: {
    #a: {
      [("x"|"y")]: int
    }
    b: (〈0;#a〉 & {
      x: 4
    })
    c: (〈0;#a〉 & {
      z: 4
    })
  }
}
--- intpattern.cue
{
  issue3670: {
    #X: {
      [int]: string
    }
    a: 〈0;#X〉
    a: {
      not_int: "c"
    }
  }
  dual: {
    _
    [string]: _
    [int]: _
  }
}
--- issue2109.cue
{
  patternCycle: {
    issue2109: {
      p1: {
        countries: [
          "US",
          "GB",
          "AU",
        ]
        FlagsURLs: {
          [or(〈1;countries〉)]: string
        }
        〈0;FlagsURLs〉
      }
    }
  }
  patternCycle: {
    issue2109: {
      p2: {
        FlagsURLs: {
          [or(〈1;countries〉)]: string
        }
        〈0;FlagsURLs〉
        countries: [
          "US",
          "GB",
          "AU",
        ]
      }
    }
  }
  patternCycle: {
    issue2109: {
      p3: {
        〈0;FlagsURLs〉
        FlagsURLs: {
          [or(〈1;countries〉)]: string
        }
        countries: [
          "US",
          "GB",
          "AU",
        ]
      }
    }
  }
  patternCycle: {
    t1: {
      p1: {
        countries: ("US"|"GB"|"AU")
        FlagsURLs: {
          [〈1;countries〉]: string
        }
        〈0;FlagsURLs〉
      }
    }
  }
  patternCycle: {
    t1: {
      p2: {
        〈0;FlagsURLs〉
        countries: ("US"|"GB"|"AU")
        FlagsURLs: {
          [〈1;countries〉]: string
        }
      }
    }
  }
  patternCycle: {
    t1: {
      p3: {
        〈0;FlagsURLs〉
        FlagsURLs: {
          [〈1;countries〉]: string
        }
        countries: ("US"|"GB"|"AU")
      }
    }
  }
}
--- multi.cue
{
  issue3638: {
    full: {
      routes: {
        default: {}
      }
      routes: {
        [string]: {
          spec: [
            {
              refs: [
                {
                  "default"
                },
              ]
            },
          ]
        }
      }
      #Fields: {
        input: _
        output: [
          for _, x in 〈1;input〉 {
            〈1;x〉
          },
        ]
        ...
      }
      #Kubernetes: {
        (〈1;#Fields〉 & {
          Routes: {
            route: {
              [_]: {
                spec!: 〈5;let HTTPROUTESPEC#1〉.#x
              }
            }
          }
        })
        〈1;#Fields〉
        Routes: _
        input: {
          res1: 〈1;Routes〉
        }
      }
      let HTTPROUTESPEC#1 = {
        #x: ([
          ...{
            refs: [
              ...{
                string
              },
            ]
          },
        ]|*[
          {
            other: []
          },
        ])
      }
      entrypoint: 〈0;used〉.output
      used: (〈0;#Kubernetes〉 & {
        Routes: {
          route: 〈2;routes〉
        }
      })
    }
  }
  issue3638: {
    reduced: {
      input: {
        k1: {
          [string]: {
            a: 1
          }
        }
      }
      input: {
        k1: {
          foo: 〈2;#x〉
        }
      }
      out: {
        for _, x in 〈1;input〉 {
          〈1;x〉
        }
        for _, x in 〈1;input〉 {
          〈1;x〉
        }
      }
      #x: ({
        a: int
      }|*{
        b: 2
      })
    }
  }
  issue3638: {
    compare: {
      input: {
        k1: {
          foo: {
            a: 1
          }
        }
      }
      input: {
        k1: {
          foo: 〈2;#x〉
        }
      }
      out: {
        for _, x in 〈1;input〉 {
          〈1;x〉
        }
        for _, x in 〈1;input〉 {
          〈1;x〉
        }
      }
      #x: ({
        a: int
      }|*{
        b: 2
      })
    }
  }
}
