//go:build go1.27

package embedlit

type A struct {
	a int
	B
}

type B struct {
	b int
	C
}

type C struct {
	c int
	D
}

type D struct {
	d int
	E
}

type E struct {
	e int
	F
}

type F struct {
	f int
}

type G struct {
	f int
}

type H struct {
	F
	G
}

type I struct {
	i int
}

type J struct {
	F
	G
	I
}

const zero = 0

type K struct{ L }
type L []int

type T struct {
	a int
	b int
	U
}

type U struct {
	x int
	V
}

type V struct {
	x, y int
}

var (
	_ = A{B: B{b: 1}}                         // want "embedded field type can be removed from struct literal"
	_ = A{a: 1, B: B{b: 1, C: C{c: 1}}}       // want "embedded field type can be removed from struct literal"
	_ = E{F: F{1}}                            // nope: cannot promote unkeyed fields
	_ = D{E: E{F: F{1}}, d: 1}                // want "embedded field type can be removed from struct literal"
	_ = D{E: E{e: 2, F: F{1}}}                // want "embedded field type can be removed from struct literal"
	_ = A{a: 10, B: B{C: C{1, D{d: 1}}}}      // want "embedded field type can be removed from struct literal"
	_ = H{F: F{f: 1}}                         // nope: cannot promote ambiguous fields
	_ = J{I: I{i: 1}, F: F{f: 1}, G: G{f: 1}} // want "embedded field type can be removed from struct literal"
	// multi-line with commas
	_ = A{ // want "embedded field type can be removed from struct literal"
		B: B{
			b: 1,
		},
	}

	_ = A{a: 1, B: B{}} // nope: empty composite lit

	// don't suggest a fix if it's too tricky to preserve comments
	_ = A{ // nope: comments within range to delete
		B: B{ // one
			C: C{ // two
				c: 1, // three
			}, // four
		}, // five
	}
	_ = A{ // nope: comments within range to delete
		B: B{b: 1 /* comment, with comma */},
		a: 2,
	}
	_ = A{ // nope: comments within range to delete
		B: B{
			b: 1, // comment, with comma
		},
		a: 2,
	}
	_ = A{B: /* comment */ B{b: 1}}       // nope: comment in range to delete
	_ = A{B: B{b: 1} /* comment */, a: 2} // want "embedded field type can be removed from struct literal"
	_ = K{L: L{zero: 0}}                  // nope: cannot promote slice elements
	_ = K{L: L{0: 100}}                   // nope: cannot promote slice elements

	_ = A{ // want "embedded field type can be removed from struct literal"
		B: B{
			C: C{
				c: 1,
			},
			b: 2,
		},
		a: 3,
	}

	_ = A{B: B{C: C{c: 1}}} // want "embedded field type can be removed from struct literal"

	_ = A{B: B{ // want "embedded field type can be removed from struct literal"
		C: C{
			c: 1,
		},
	}}

	_ = A{ // want "embedded field type can be removed from struct literal"
		B: B{C: C{c: 1}},
	}

	_ = A{ // want "embedded field type can be removed from struct literal"
		B: B{ // comment here
			C: C{
				c: 1,
			},
		},
	}

	_ = E{ // want "embedded field type can be removed from struct literal"
		e: 2,
		F: F{f: 1,
		},
	}
)

func _() {
	t1 := A{a: 1} // want "embedded field assignment can be moved to struct literal"
	t1.b = 2

	var t2 A
	t2 = A{a: 1} // want "embedded field assignment can be moved to struct literal"
	t2.b = 2

	var t3 = A{a: 1} // want "embedded field assignment can be moved to struct literal"
	t3.b = 2

	t4 := T{1, 2, U{x: 3}} // nope: can't mix keyed and unkeyed elements
	t4.x = 4

	t5 := A{a: 1}
	_ = t5 // nope: intervening statement
	t5.b = 2

	t6 := A{a: 1} // nope: value assigned depends on t6 itself
	t6.b = t6.a + 1

	t7 := A{a: 1} // want "embedded field assignment can be moved to struct literal"
	t7.b = foo()

	// Only apply edits from pattern A first even though both patterns apply.
	t8 := A{ // want "embedded field type can be removed from struct literal"
		B: B{b: 1},
	}
	t8.a = 2

	t9 := A{} // nope: multiple assignments not yet supported
	t9.a, t9.b = 1, 2

	t10 := A{} // want "embedded field assignment can be moved to struct literal"
	t10.a = 1  // this comment is preserved
	t10.b = 2  // this one too

	t11 := A{a: 1} // want "embedded field assignment can be moved to struct literal"
	t11.b = 2
	t11.c = 3

	t12 := A{} // want "embedded field assignment can be moved to struct literal"
	t12.B = B{
		b: 1, // this comment is preserved
	}

	t13 := A{} // want "embedded field assignment can be moved to struct literal"
	t13.a = 1
	// comment between assignments
	t13.b = 2

	t14 := A{} // want "embedded field assignment can be moved to struct literal"
	t14.a = 1
	t14.b =
		foo() +
			1

	t15 := A{a: 1} // nope: += in field assignment
	t15.b += 2

	t16 := A{ // want "embedded field assignment can be moved to struct literal"
		a: 1, // comment, with a comma
	}
	t16.b = 2

	assgn1, assgn2 := A{}, A{} // nope, multi-assign
	assgn1.e = 1
	assgn2.e = 1

	var (
		v        = A{} // nope, multi-declaration
		othervar = 2
	)
	v.e = 1
	_ = othervar

	var v1, v2 = A{}, A{} // nope, multi-declaration
	v2.e = 1
	_ = v1

	t17 := U{}
	t17.x = 1 // nope: not embedded

	t18 := V{}
	t18.x = 1
	t18.y = 2 // nope: not embedded

	t19 := U{} // want "embedded field assignment can be moved to struct literal"
	t19.y = 2
	t19.x = 1
}

func foo() int {
	return 0
}
