// Copyright 2025 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package slicesbackward

import (
	"slices"
	"sync"
)

var _ = slices.Backward[[]int] // force import of "slices" to avoid duplicate import edits

// Basic: only use of i is s[i] — should suggest "for _, v := range slices.Backward(s)"
func onlyIndexUse(s []int) {
	for i := len(s) - 1; i >= 0; i-- { // want "backward loop over slice can be modernized using slices.Backward"
		println(s[i])
	}
}

// Tests for the heuristic that generates a name for the value variable.

// Should use "name" for the value variable, and should remove the declaration.
func firstUseName(s []int) {
	for i := len(s) - 1; i >= 0; i-- { // want "backward loop over slice can be modernized using slices.Backward"
		name := s[i]
		println(name)
		name2 := s[i]
		println(name2)
	}
}

func useName(s []int) {
	for i := len(s) - 1; i >= 0; i-- { // want "backward loop over slice can be modernized using slices.Backward"
		println(i)
		name := s[i]
		println(name)
	}
}

// Can't use "name" because it is declared outside the block, and no singular
// form of "arr", so use the first letter ("a").
func firstLetter(arr []int) {
	name := 2
	for i := len(arr) - 1; i >= 0; i-- { // want "backward loop over slice can be modernized using slices.Backward"
		name = arr[i]
	}
	println(name)
}

// Slice expression is a selector and not a valid identifier.
// Should fallback to "v" instead of trying to use "s.item".
func indexUseSelector(s struct{ items []int }) {
	for i := len(s.items) - 1; i >= 0; i-- { // want "backward loop over slice can be modernized using slices.Backward"
		println(s.items[i])
	}
}

// Index used for something other than s[i] — keep both i and v.
func indexUsedElsewhere(s []int) {
	for i := len(s) - 1; i >= 0; i-- { // want "backward loop over slice can be modernized using slices.Backward"
		println(i, s[i])
	}
}

// Index used only for non-slice purpose (no s[i] at all).
func indexNoSliceAccess(s []int) {
	for i := len(s) - 1; i >= 0; i-- { // want "backward loop over slice can be modernized using slices.Backward"
		println(i)
	}
}

// Should NOT fire: condition is i > 0, not i >= 0.
func condGT(s []int) {
	for i := len(s) - 1; i > 0; i-- {
		println(s[i])
	}
}

// Should NOT fire: post is i -= 2.
func postNotDec(s []int) {
	for i := len(s) - 1; i >= 0; i -= 2 {
		println(s[i])
	}
}

// Should NOT fire: init is not len(s) - 1.
func initNotLenMinus1(s []int) {
	for i := len(s) - 2; i >= 0; i-- {
		println(s[i])
	}
}

// Should NOT fire: i is assigned inside the body.
func indexAssignedInBody(s []int) {
	for i := len(s) - 1; i >= 0; i-- {
		i = i - 1 // nolint: ignore for test
		println(s[i])
	}
}

// Should work with a named slice variable.
func namedSlice() {
	nums := []string{"a", "b", "c"}
	for i := len(nums) - 1; i >= 0; i-- { // want "backward loop over slice can be modernized using slices.Backward"
		println(nums[i])
	}
}

// Should fire: i is declared before the loop but not address-taken.
func iDeclaredBeforeLoop(s []int) {
	var i int
	for i = len(s) - 1; i >= 0; i-- { // want "backward loop over slice can be modernized using slices.Backward"
		println(s[i])
	}
	_ = i
}

// Should NOT fire: i is address-taken before the loop (init uses =, not :=).
func iAddressTakenBeforeLoop(s []int) {
	var i int
	p := &i
	for i = len(s) - 1; i >= 0; i-- {
		println(s[i])
	}
	_ = p
}

// Should NOT fire: an index expression is used as an lvalue (slice mutation)
func indexExprAssign(s []int) {
	for i := len(s) - 1; i >= 0; i-- {
		_ = s[i]
		s[i] = 0
	}
}

// Should NOT fire: an index expression is used as an lvalue (slice mutation)
func indexExprAssignWithOp(s []int) {
	for i := len(s) - 1; i >= 0; i-- {
		_ = s[i]
		s[i] += 1
		s[i] -= 1
		s[i] *= 1
		s[i] /= 1
	}
}

// Should NOT fire: an index expression is used as an lvalue (slice mutation)
func indexExprIncDec(s []int) {
	for i := len(s) - 1; i >= 0; i-- {
		_ = s[i]
		s[i]++
		s[i]--
	}
}

// Should NOT fire: an index expression is address-taken
func indexExprAddr(s []int) {
	for i := len(s) - 1; i >= 0; i-- {
		_ = &s[i]
	}
}

type item struct {
	n int
}

// Should NOT fire: field mutation
func indexExprMutated(s []item) {
	for i := len(s) - 1; i >= 0; i-- {
		s[i].n++
	}
}

type outer struct {
	inner item
}

// Should NOT fire: nested field mutation
func indexExprNestedField(s []outer) {
	for i := len(s) - 1; i >= 0; i-- {
		s[i].inner.n++
	}
}

// Should NOT fire: field assignment
func indexExprFieldAssign(s []item) {
	for i := len(s) - 1; i >= 0; i-- {
		s[i].n = 5
	}
}

// Should NOT fire: multi-field assignment
func indexExprMultiAssign(s []int) {
	for i := len(s) - 2; i >= 0; i-- {
		s[i], s[i+1] = 1, 2
	}
}

// Should NOT fire: parenthesized assignment
func indexExprParenthesizedAssign(s []int) {
	for i := len(s) - 1; i >= 0; i-- {
		(s[i]) = 5
	}
}

// Should NOT fire: address-taken
func indexExprFieldAddr(s []item) {
	for i := len(s) - 1; i >= 0; i-- {
		_ = &s[i].n
	}
}

// Should NOT fire: method call with pointer receiver may mutate element
func indexExprMethodCall(s []sync.Mutex) {
	for i := len(s) - 1; i >= 0; i-- {
		s[i].Lock()
		s[i].Unlock()
	}
}

type stringer struct{}

func (stringer) String() string { return "" }

func (*stringer) StringPtr() string { return "" }

// SHOULD fire: method call with value receiver does not mutate element
func indexExprValueReceiver(s []stringer) {
	for i := len(s) - 1; i >= 0; i-- { // want "backward loop over slice can be modernized using slices.Backward"
		_ = s[i].String()
	}
}

// SHOULD fire: method call with pointer receiver type and pointer receiver value does not mutate
func indexExprPtrValueReceiver(s []*stringer) {
	for i := len(s) - 1; i >= 0; i-- { // want "backward loop over slice can be modernized using slices.Backward"
		_ = s[i].StringPtr()
	}
}

// Should NOT fire: range assignment mutating slice elements
func indexExprRangeAssign(s []int, x []int) {
	for i := len(s) - 1; i >= 0; i-- {
		for s[i] = range x {
		}
	}
}

// SHOULD fire: slice of slices is indirect reference
func indexExprSliceOfSlices(s [][]int) {
	for i := len(s) - 1; i >= 0; i-- { // want "backward loop over slice can be modernized using slices.Backward"
		s[i][0]++
	}
}

// SHOULD fire: slice of pointers is indirect reference
func indexExprPointerSlice(s []*item) {
	for i := len(s) - 1; i >= 0; i-- { // want "backward loop over slice can be modernized using slices.Backward"
		s[i].n++
	}
}

// Should NOT fire: slice of arrays, mutating array element is direct mutation
func indexExprSliceOfArrays(s [][3]int) {
	for i := len(s) - 1; i >= 0; i-- {
		s[i][0]++
	}
}

// SHOULD fire: index expression used as index of another slice which is mutated
func indexExprUsedAsIndex(s []int) {
	for i := len(s) - 1; i >= 0; i-- { // want "backward loop over slice can be modernized using slices.Backward"
		s[s[i]] = 5
	}
}
