package action

import (
	"context"
	"fmt"
	"regexp"
	"runtime"
	"strings"

	fishcomp "github.com/gopasspw/gopass/internal/completion/fish"
	zshcomp "github.com/gopasspw/gopass/internal/completion/zsh"
	"github.com/gopasspw/gopass/internal/out"
	"github.com/gopasspw/gopass/internal/tree"
	"github.com/gopasspw/gopass/pkg/ctxutil"
	"github.com/urfave/cli/v3"
)

var escapeRegExp = regexp.MustCompile(`('|"|\s|\(|\)|\<|\>|\&|\;|\#|\\|\||\*|\?)`)

// bashEscape Escape special characters with `\`.
func bashEscape(s string) string {
	return escapeRegExp.ReplaceAllStringFunc(s, func(c string) string {
		if c == `\` {
			return `\\\\`
		}

		if c == `'` {
			return `\` + c
		}

		if c == `"` {
			return `\\\` + c
		}

		return `\\` + c
	})
}

// Complete prints a list of all password names to os.Stdout, for bash completion.
func (s *miscHandler) Complete(ctx context.Context, cmd *cli.Command) {
	ctx = ctxutil.WithGlobalFlags(ctx, cmd)
	_, err := s.Store.IsInitialized(ctx) // important to make sure the structs are not nil.
	if err != nil {
		out.Errorf(ctx, "Store not initialized: %s", err)

		return
	}
	list, err := s.Store.List(ctx, tree.INF)
	if err != nil {
		return
	}

	for _, v := range list {
		fmt.Fprintln(stdout, bashEscape(v))
	}
}

// CompletionOpenBSDKsh returns an OpenBSD ksh script used for auto completion.
func (s *miscHandler) CompletionOpenBSDKsh(a *cli.Command) error {
	out := `
PASS_LIST=$(gopass ls -f)
set -A complete_gopass -- $PASS_LIST %s
`

	if a == nil {
		return fmt.Errorf("can not parse command options")
	}

	opts := make([]string, 0, len(a.Commands))
	for _, opt := range a.Commands {
		opts = append(opts, opt.Name)
		if len(opt.Aliases) > 0 {
			opts = append(opts, strings.Join(opt.Aliases, " "))
		}
	}

	fmt.Fprintf(stdout, out, strings.Join(opts, " "))

	return nil
}

// CompletionBash returns a bash script used for auto completion.
func (s *miscHandler) CompletionBash(ctx context.Context, cmd *cli.Command) error {
	out := `_gopass_bash_autocomplete() {
     local cur opts base
     COMPREPLY=()
     cur="${COMP_WORDS[COMP_CWORD]}"
     # Use error handling to prevent crashes from invalid flags
     opts=$( ${COMP_WORDS[@]:0:$COMP_CWORD} --generate-shell-completion 2>/dev/null ) || opts=""
     local IFS=$'\n'
     COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
     return 0
 }

`
	out += "complete -F _gopass_bash_autocomplete " + s.Name
	if runtime.GOOS == "windows" {
		out += "\ncomplete -F _gopass_bash_autocomplete " + s.Name + ".exe"
	}
	fmt.Fprintln(stdout, out)

	return nil
}

// CompletionFish returns an autocompletion script for fish.
func (s *miscHandler) CompletionFish(a *cli.Command) error {
	if a == nil {
		return fmt.Errorf("app is nil")
	}
	comp, err := fishcomp.GetCompletion(a)
	if err != nil {
		return err
	}

	fmt.Fprintln(stdout, comp)

	return nil
}

// CompletionZSH returns a zsh completion script.
func (s *miscHandler) CompletionZSH(a *cli.Command) error {
	comp, err := zshcomp.GetCompletion(a)
	if err != nil {
		return err
	}

	fmt.Fprintln(stdout, comp)

	return nil
}
