package pgconn

import (
	
	
)

// authMethod is one of the method keywords accepted by libpq's require_auth.
type authMethod uint8

const (
	authMethodPassword authMethod = iota
	authMethodMD5
	authMethodGSS
	authMethodSSPI
	authMethodSCRAMSHA256
	authMethodOAuth
	authMethodNone
	authMethodCount
)

var authMethodNames = [authMethodCount]string{
	authMethodPassword:    "password",
	authMethodMD5:         "md5",
	authMethodGSS:         "gss",
	authMethodSSPI:        "sspi",
	authMethodSCRAMSHA256: "scram-sha-256",
	authMethodOAuth:       "oauth",
	authMethodNone:        "none",
}

// requireAuth is the parsed form of the require_auth connection parameter. It mirrors libpq's
// auth_required / allowed_auth_methods bookkeeping (see fe-connect.c, conn->allowed_auth_methods).
type requireAuth struct {
	// raw is the original parameter value, used in error messages.
	raw string

	// authRequired is true when the server must complete an authentication exchange before sending
	// AuthenticationOk. It is false when the parameter is unset, fully negated, or "none" is in the
	// allowed set.
	authRequired bool

	// allowed is a bitmask of permitted authMethod values.
	allowed uint8
}

func ( requireAuth) ( authMethod) bool {
	return .allowed&(1<<) != 0
}

// check returns an error if method m is not permitted by the policy. The reason is phrased to
// follow libpq's "authentication method requirement \"%s\" failed: %s" form so users migrating
// from libpq see familiar diagnostics.
func ( requireAuth) ( authMethod) error {
	if .allows() {
		return nil
	}
	var  string
	if  == authMethodNone {
		 = "server did not complete authentication"
	} else {
		 = fmt.Sprintf("server requested %s authentication", authMethodNames[])
	}
	return fmt.Errorf("authentication method requirement %q failed: %s", .raw, )
}

// parseRequireAuth parses the require_auth connection parameter with libpq-compatible semantics:
// a comma-separated list of method names, optionally each prefixed with "!" to negate. Negated and
// non-negated entries cannot be mixed; duplicate entries are rejected. An empty string yields a
// permissive policy (all methods allowed, no authentication required).
func ( string) (requireAuth, error) {
	 := requireAuth{raw: }

	if  == "" {
		.allowed = 1<<authMethodCount - 1
		return , nil
	}

	 := true
	 := false
	for  := range strings.SplitSeq(, ",") {
		 := strings.TrimSpace()

		 := strings.HasPrefix(, "!")
		if  {
			 = [1:]
		}
		if  {
			 = 
			if  {
				// A negated list starts from "everything allowed, auth not required" and removes
				// methods; "!none" below flips authRequired back on.
				.allowed = 1<<authMethodCount - 1
			} else {
				.authRequired = true
			}
			 = false
		} else if  !=  {
			if  {
				return requireAuth{}, fmt.Errorf("negative require_auth method %q cannot be mixed with non-negative methods", )
			}
			return requireAuth{}, fmt.Errorf("require_auth method %q cannot be mixed with negative methods", )
		}

		var  authMethod
		switch  {
		case "password":
			 = authMethodPassword
		case "md5":
			 = authMethodMD5
		case "gss":
			 = authMethodGSS
		case "sspi":
			 = authMethodSSPI
		case "scram-sha-256":
			 = authMethodSCRAMSHA256
		case "oauth":
			 = authMethodOAuth
		case "none":
			 = authMethodNone
		default:
			return requireAuth{}, fmt.Errorf("invalid require_auth method: %q", )
		}

		 := uint8(1) << 
		if  {
			if .allowed& == 0 {
				return requireAuth{}, fmt.Errorf("require_auth method %q is specified more than once", )
			}
			.allowed &^= 
			if  == authMethodNone {
				.authRequired = true
			}
		} else {
			if .allowed& != 0 {
				return requireAuth{}, fmt.Errorf("require_auth method %q is specified more than once", )
			}
			.allowed |= 
			if  == authMethodNone {
				.authRequired = false
			}
		}
	}

	return , nil
}