package common

import (
	"bufio"
	"crypto/rand"
	"io"
	"math/big"
	mrand "math/rand"
	"strings"
)

func AddStringSliceItem(item string, slice *[]string) {
	for _, i := range *slice {
		if i == item {
			return
		}
	}
	*slice = append(*slice, item)
}

func RemoveStringSliceItem(item string, slice *[]string) {
	for i, listItem := range *slice {
		if item == listItem {
			(*slice)[i] = (*slice)[len(*slice)-1]
			*slice = (*slice)[:len(*slice)-1]
		}
	}
}

func ResponseContainsSubstring(reader io.ReadCloser, target string) (bool, error) {
	scanner := bufio.NewScanner(reader)
	for scanner.Scan() {
		if strings.Contains(scanner.Text(), target) {
			return true, nil
		}
	}
	if err := scanner.Err(); err != nil {
		return false, err
	}
	return false, nil
}

var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")

// RandomID returns a random string of the given length.
func RandomID(length int) string {
	b := make([]rune, length)
	for i := range b {
		randInt, _ := rand.Int(rand.Reader, big.NewInt(int64(len(letters))))
		b[i] = letters[randInt.Int64()]
	}

	return string(b)
}

// SliceContainsString searches a slice to determine if it contains a specified string.
// Returns the index of the first match in the slice.
func SliceContainsString(origTargetString string, stringSlice []string, ignoreCase bool) (bool, string, int) {
	targetString := origTargetString
	if ignoreCase {
		targetString = strings.ToLower(origTargetString)
	}
	for i, origStringFromSlice := range stringSlice {
		stringFromSlice := origStringFromSlice
		if ignoreCase {
			stringFromSlice = strings.ToLower(origStringFromSlice)
		}
		if targetString == stringFromSlice {
			return true, targetString, i
		}
	}
	return false, "", 0
}

// GoFakeIt Password generator does not guarantee inclusion of characters.
// Using a custom random password generator with guaranteed inclusions (atleast) of lower, upper, numeric and special characters
func GenerateRandomPassword(lower, upper, numeric, special bool, length int) string {
	if length < 1 {
		return ""
	}

	var password []rune
	var required []rune
	var allowed []rune

	lowerChars := []rune("abcdefghijklmnopqrstuvwxyz")
	upperChars := []rune("ABCDEFGHIJKLMNOPQRSTUVWXYZ")
	specialChars := []rune("!@#$%^&*()-_=+[]{}|;:',.<>?/")
	numberChars := []rune("0123456789")

	// Ensure inclusion from each requested category
	if lower {
		rand, _ := rand.Int(rand.Reader, big.NewInt(int64(len(lowerChars))))
		ch := lowerChars[rand.Int64()]
		required = append(required, ch)
		allowed = append(allowed, lowerChars...)
	}
	if upper {
		rand, _ := rand.Int(rand.Reader, big.NewInt(int64(len(upperChars))))
		ch := upperChars[rand.Int64()]
		required = append(required, ch)
		allowed = append(allowed, upperChars...)
	}
	if numeric {
		rand, _ := rand.Int(rand.Reader, big.NewInt(int64(len(numberChars))))
		ch := numberChars[rand.Int64()]
		required = append(required, ch)
		allowed = append(allowed, numberChars...)
	}
	if special {
		rand, _ := rand.Int(rand.Reader, big.NewInt(int64(len(specialChars))))
		ch := specialChars[rand.Int64()]
		required = append(required, ch)
		allowed = append(allowed, specialChars...)
	}

	if len(allowed) == 0 {
		return "" // No character sets enabled
	}

	// Fill the rest of the password
	for i := 0; i < length-len(required); i++ {
		rand, _ := rand.Int(rand.Reader, big.NewInt(int64(len(allowed))))
		ch := allowed[rand.Int64()]
		password = append(password, ch)
	}

	// Combine required and random characters, then shuffle
	password = append(password, required...)
	mrand.Shuffle(len(password), func(i, j int) {
		password[i], password[j] = password[j], password[i]
	})

	return string(password)
}
