package yqlib

import (
	"testing"
)

var expectedWhereIsMyCommentMapKey = `D0, P[], (!!seq)::- p: ""
  isKey: false
  hc: ""
  lc: ""
  fc: ""
- p: hello
  isKey: true
  hc: ""
  lc: hello-world-comment
  fc: ""
- p: hello
  isKey: false
  hc: ""
  lc: ""
  fc: ""
- p: hello.message
  isKey: true
  hc: ""
  lc: ""
  fc: ""
- p: hello.message
  isKey: false
  hc: ""
  lc: ""
  fc: ""
`

var expectedWhereIsMyCommentArray = `D0, P[], (!!seq)::- p: ""
  isKey: false
  hc: ""
  lc: ""
  fc: ""
- p: name
  isKey: true
  hc: ""
  lc: ""
  fc: ""
- p: name
  isKey: false
  hc: ""
  lc: ""
  fc: ""
- p: name.0
  isKey: false
  hc: under-name-comment
  lc: ""
  fc: ""
`

var commentOperatorScenarios = []expressionScenario{
	{
		description:    "Set line comment",
		subdescription: "Set the comment on the key node for more reliability (see below).",
		document:       `a: cat`,
		expression:     `.a line_comment="single"`,
		expected: []string{
			"D0, P[], (!!map)::a: cat # single\n",
		},
	},
	{
		description:    "Set line comment of a maps/arrays",
		subdescription: "For maps and arrays, you need to set the line comment on the _key_ node. This will also work for scalars.",
		document:       "a:\n  b: things",
		expression:     `(.a | key) line_comment="single"`,
		expected: []string{
			"D0, P[], (!!map)::a: # single\n    b: things\n",
		},
	},
	{
		skipDoc:    true,
		document:   "a: cat\nb: dog",
		expression: `.a line_comment=.b`,
		expected: []string{
			"D0, P[], (!!map)::a: cat # dog\nb: dog\n",
		},
	},
	{
		skipDoc:    true,
		document:   "a: cat\n---\na: dog",
		expression: `.a line_comment |= documentIndex`,
		expected: []string{
			"D0, P[], (!!map)::a: cat # 0\n",
			"D1, P[], (!!map)::a: dog # 1\n",
		},
	},
	{
		description: "Use update assign to perform relative updates",
		document:    "a: cat\nb: dog",
		expression:  `.. line_comment |= .`,
		expected: []string{
			"D0, P[], (!!map)::a: cat # cat\nb: dog # dog\n",
		},
	},
	{
		skipDoc:    true,
		document:   "a: cat\nb: dog",
		expression: `.. comments |= .`,
		expected: []string{
			"D0, P[], (!!map)::a: cat # cat\n# cat\n\n# cat\nb: dog # dog\n# dog\n\n# dog\n",
		},
	},
	{
		description:    "Where is the comment - map key example",
		subdescription: "The underlying yaml parser can assign comments in a document to surprising nodes. Use an expression like this to find where you comment is. 'p' indicates the path, 'isKey' is if the node is a map key (as opposed to a map value).\nFrom this, you can see the 'hello-world-comment' is actually on the 'hello' key",
		document:       "hello: # hello-world-comment\n  message: world",
		expression:     `[... | {"p": path | join("."), "isKey": is_key, "hc": headComment, "lc": lineComment, "fc": footComment}]`,
		expected: []string{
			expectedWhereIsMyCommentMapKey,
		},
	},
	{
		description:    "Retrieve comment - map key example",
		subdescription: "From the previous example, we know that the comment is on the 'hello' _key_ as a lineComment",
		document:       "hello: # hello-world-comment\n  message: world",
		expression:     `.hello | key | line_comment`,
		expected: []string{
			"D0, P[hello], (!!str)::hello-world-comment\n",
		},
	},
	{
		description:    "Where is the comment - array example",
		subdescription: "The underlying yaml parser can assign comments in a document to surprising nodes. Use an expression like this to find where you comment is. 'p' indicates the path, 'isKey' is if the node is a map key (as opposed to a map value).\nFrom this, you can see the 'under-name-comment' is actually on the first child",
		document:       "name:\n  # under-name-comment\n  - first-array-child",
		expression:     `[... | {"p": path | join("."), "isKey": is_key, "hc": headComment, "lc": lineComment, "fc": footComment}]`,
		expected: []string{
			expectedWhereIsMyCommentArray,
		},
	},
	{
		description:    "Retrieve comment - array example",
		subdescription: "From the previous example, we know that the comment is on the first child as a headComment",
		document:       "name:\n  # under-name-comment\n  - first-array-child",
		expression:     `.name[0] | headComment`,
		expected: []string{
			"D0, P[name 0], (!!str)::under-name-comment\n",
		},
	},
	{
		description: "Set head comment",
		document:    `a: cat`,
		expression:  `. head_comment="single"`,
		expected: []string{
			"D0, P[], (!!map)::# single\na: cat\n",
		},
	},
	{
		description: "Set head comment of a map entry",
		document:    "f: foo\na:\n  b: cat",
		expression:  `(.a | key) head_comment="single"`,
		expected: []string{
			"D0, P[], (!!map)::f: foo\n# single\na:\n    b: cat\n",
		},
	},
	{
		description: "Set foot comment, using an expression",
		document:    `a: cat`,
		expression:  `. foot_comment=.a`,
		expected: []string{
			"D0, P[], (!!map)::a: cat\n# cat\n",
		},
	},
	{
		skipDoc:     true,
		description: "Set foot comment, using an expression",
		document:    "a: cat\n\n# hi",
		expression:  `. foot_comment=""`,
		expected: []string{
			"D0, P[], (!!map)::a: cat\n",
		},
	},
	{
		skipDoc:    true,
		document:   `a: cat`,
		expression: `. foot_comment=.b.d`,
		expected: []string{
			"D0, P[], (!!map)::a: cat\n",
		},
	},
	{
		skipDoc:    true,
		document:   `a: cat`,
		expression: `. foot_comment|=.b.d`,
		expected: []string{
			"D0, P[], (!!map)::a: cat\n",
		},
	},
	{
		description: "Remove comment",
		document:    "a: cat # comment\nb: dog # leave this",
		expression:  `.a line_comment=""`,
		expected: []string{
			"D0, P[], (!!map)::a: cat\nb: dog # leave this\n",
		},
	},
	{
		description:    "Remove (strip) all comments",
		subdescription: "Note the use of `...` to ensure key nodes are included.",
		document:       "# hi\n\na: cat # comment\n\n# great\n\nb: # key comment",
		expression:     `... comments=""`,
		expected: []string{
			"D0, P[], (!!map)::a: cat\nb:\n",
		},
	},
	{
		description: "Get line comment",
		document:    "# welcome!\n\na: cat # meow\n\n# have a great day",
		expression:  `.a | line_comment`,
		expected: []string{
			"D0, P[a], (!!str)::meow\n",
		},
	},
	{
		description:           "Get head comment",
		dontFormatInputForDoc: true,
		document:              "# welcome!\n\na: cat # meow\n\n# have a great day",
		expression:            `. | head_comment`,
		expected: []string{
			"D0, P[], (!!str)::welcome!\n\n",
		},
	},
	{
		skipDoc:     true,
		description: "strip trailing comment recurse all",
		document:    "a: cat\n\n# haha",
		expression:  `... comments= ""`,
		expected: []string{
			"D0, P[], (!!map)::a: cat\n",
		},
	},
	{
		skipDoc:     true,
		description: "strip trailing comment recurse values",
		document:    "a: cat\n\n# haha",
		expression:  `.. comments= ""`,
		expected: []string{
			"D0, P[], (!!map)::a: cat\n",
		},
	},
	{
		description:           "Head comment with document split",
		dontFormatInputForDoc: true,
		document:              "# welcome!\n---\n# bob\na: cat # meow\n\n# have a great day",
		expression:            `head_comment`,
		expected: []string{
			"D0, P[], (!!str)::welcome!\nbob\n",
		},
	},
	{
		description:           "Get foot comment",
		dontFormatInputForDoc: true,
		document:              "# welcome!\n\na: cat # meow\n\n# have a great day\n# no really",
		expression:            `. | foot_comment`,
		expected: []string{
			"D0, P[], (!!str)::have a great day\nno really\n",
		},
	},
	{
		description: "leading spaces",
		skipDoc:     true,
		document:    " # hi",
		expression:  `.`,
		expected: []string{
			"D0, P[], (!!null):: # hi\n",
		},
	},
	{
		description: "string spaces",
		skipDoc:     true,
		document:    "# hi\ncat\n",
		expression:  `.`,
		expected: []string{
			"D0, P[], (!!str)::# hi\ncat\n",
		},
	},
	{
		description: "leading spaces with new line",
		skipDoc:     true,
		document:    " # hi\n",
		expression:  `.`,
		expected: []string{
			"D0, P[], (!!null):: # hi\n",
		},
	},
	{
		description: "directive",
		skipDoc:     true,
		document:    "%YAML 1.1\n# hi\n",
		expression:  `.`,
		expected: []string{
			"D0, P[], (!!null)::%YAML 1.1\n# hi\n",
		},
	},
}

func TestCommentOperatorScenarios(t *testing.T) {
	for _, tt := range commentOperatorScenarios {
		testScenario(t, &tt)
	}
	documentOperatorScenarios(t, "comment-operators", commentOperatorScenarios)
}
