// Copyright 2023 Dolthub, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package cli

import (
	"fmt"
	"os"

	"golang.org/x/crypto/ssh/terminal"

	"github.com/dolthub/dolt/go/libraries/doltcore/dconfig"
	"github.com/dolthub/dolt/go/libraries/utils/argparser"
)

type UserPassword struct {
	Username  string
	Password  string
	Specified bool // If true, the user and password were provided by the user.
}

// BuildUserPasswordPrompt builds a UserPassword struct from the parsed args. The user is prompted for a password if one
// is not provided. If a username is not provided, the default is "root" (which will not be allowed is a password is
// provided). A new instances of ArgParseResults is returned which does not contain the user or password flags.
func BuildUserPasswordPrompt(parsedArgs *argparser.ArgParseResults) (newParsedArgs *argparser.ArgParseResults, credentials *UserPassword, err error) {
	userId, hasUserId := parsedArgs.GetValue(UserFlag)
	if !hasUserId {
		envUser, hasEnvUser := os.LookupEnv(dconfig.EnvUser)
		if hasEnvUser {
			userId = envUser
			hasUserId = true
		}
	}

	password, hasPassword := parsedArgs.GetValue(PasswordFlag)
	if !hasPassword {
		envPassword, hasEnvPassword := os.LookupEnv(dconfig.EnvPassword)
		if hasEnvPassword {
			password = envPassword
			hasPassword = true
		}
	}

	newParsedArgs = parsedArgs.DropValue(UserFlag)
	newParsedArgs = newParsedArgs.DropValue(PasswordFlag)

	if !hasUserId && !hasPassword {
		// Common "out of box" behavior.
		return newParsedArgs, &UserPassword{Username: "root", Password: "", Specified: false}, nil
	}

	if hasUserId && hasPassword {
		return newParsedArgs, &UserPassword{Username: userId, Password: password, Specified: true}, nil
	}

	if hasUserId && !hasPassword {
		password = ""
		val, hasVal := os.LookupEnv(dconfig.EnvPassword)
		if hasVal {
			password = val
		} else {
			Printf("Enter password: ")
			passwordBytes, err := terminal.ReadPassword(int(os.Stdin.Fd()))
			if err != nil {
				return nil, nil, err
			}
			password = string(passwordBytes) // Assuming UTF-8 for time being. This may not work forever.
		}
		return newParsedArgs, &UserPassword{Username: userId, Password: password, Specified: true}, nil
	}

	testOverride, hasTestOverride := os.LookupEnv(dconfig.EnvSilenceUserReqForTesting)
	if hasTestOverride && testOverride == "Y" {
		// Used for BATS testing only. Typical usage will not hit this path, but we have many legacy tests which
		// do not provide a user, and the DOLT_ENV_PWD is set to avoid the prompt.
		return newParsedArgs, &UserPassword{Specified: false}, nil
	}

	return nil, nil, fmt.Errorf("When a password is provided, a user must also be provided. Use the --user flag to provide a username")
}
