/*
Copyright 2026 The Flux authors

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 main

import (
	"strings"
	"testing"
)

func TestBootstrapValidate_signingFlags(t *testing.T) {
	tests := []struct {
		name       string
		gpgRing    string
		gpgPass    string
		sshKey     string
		sshPass    string
		sshPassp   string
		privateKey string
		reuse      bool
		wantErr    string
	}{
		{name: "no signing flags is valid"},
		{name: "GPG only is valid", gpgRing: "./testdata/bootstrap/gpg.pgp"},
		{name: "SSH only is valid", sshKey: "./testdata/bootstrap/ed25519.private"},
		{
			name:       "Reuse-private-key with private-key-file is valid",
			privateKey: "./testdata/bootstrap/ed25519.private",
			reuse:      true,
		},
		{
			name:    "GPG + SSH errors",
			gpgRing: "./testdata/bootstrap/gpg.pgp",
			sshKey:  "./testdata/bootstrap/ed25519.private",
			wantErr: "--gpg-* and --ssh-signing-* are mutually exclusive",
		},
		{
			name:       "GPG + reuse errors",
			gpgRing:    "./testdata/bootstrap/gpg.pgp",
			privateKey: "./testdata/bootstrap/ed25519.private",
			reuse:      true,
			wantErr:    "--gpg-* and --ssh-signing-* are mutually exclusive",
		},
		{
			name:       "SSH key-file + reuse errors",
			sshKey:     "./testdata/bootstrap/ed25519.private",
			privateKey: "./testdata/bootstrap/ed25519.private",
			reuse:      true,
			wantErr:    "--ssh-signing-key-file and --ssh-signing-reuse-private-key are mutually exclusive",
		},
		{
			name:    "Reuse without private-key-file errors",
			reuse:   true,
			wantErr: "--ssh-signing-reuse-private-key requires --private-key-file",
		},
		{
			name:    "SSH password without key errors",
			sshPass: "secret",
			wantErr: "--ssh-signing-password requires --ssh-signing-key-file",
		},
		{
			name:     "SSH passphrase alias alone applies",
			sshKey:   "./testdata/bootstrap/ed25519-encrypted.private",
			sshPassp: "abcde12345",
		},
		{
			name:     "SSH password and passphrase with same value passes",
			sshKey:   "./testdata/bootstrap/ed25519-encrypted.private",
			sshPass:  "abcde12345",
			sshPassp: "abcde12345",
		},
		{
			name:     "SSH password and passphrase with different values errors",
			sshKey:   "./testdata/bootstrap/ed25519-encrypted.private",
			sshPass:  "right",
			sshPassp: "wrong",
			wantErr:  "are aliases; do not pass both",
		},
		{
			name:    "SSH malformed key fails pre-flight",
			sshKey:  "./testdata/bootstrap/malformed.private",
			wantErr: "invalid SSH signing key",
		},
		{
			name:    "SSH encrypted key without password fails pre-flight",
			sshKey:  "./testdata/bootstrap/ed25519-encrypted.private",
			wantErr: "passphrase required",
		},
		// The GPG fixture used here is encrypted (passphrase: "right") so that
		// passing the wrong passphrase exercises the Decrypt error path.
		// An unencrypted key would make Decrypt a no-op regardless of the
		// passphrase supplied.
		{
			name:    "GPG with wrong passphrase fails pre-flight",
			gpgRing: "./testdata/bootstrap/gpg-encrypted.pgp",
			gpgPass: "wrong",
			wantErr: "invalid GPG signing key",
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			savedDefaultComponents := bootstrapArgs.defaultComponents
			savedGpgRing := bootstrapArgs.gpgKeyRingPath
			savedGpgPass := bootstrapArgs.gpgPassphrase
			savedSshKey := bootstrapArgs.sshSigningKeyFile
			savedSshPass := bootstrapArgs.sshSigningPassword
			savedSshPassp := bootstrapArgs.sshSigningPassphrase
			savedPrivKey := bootstrapArgs.privateKeyFile
			savedReuse := bootstrapArgs.sshSigningReusePrivateKey
			defer func() {
				bootstrapArgs.defaultComponents = savedDefaultComponents
				bootstrapArgs.gpgKeyRingPath = savedGpgRing
				bootstrapArgs.gpgPassphrase = savedGpgPass
				bootstrapArgs.sshSigningKeyFile = savedSshKey
				bootstrapArgs.sshSigningPassword = savedSshPass
				bootstrapArgs.sshSigningPassphrase = savedSshPassp
				bootstrapArgs.privateKeyFile = savedPrivKey
				bootstrapArgs.sshSigningReusePrivateKey = savedReuse
			}()

			// The e2e TestMain calls resetCmdArgs which clears the
			// cobra-populated default components, so seed them here to
			// satisfy the requiredComponents pre-check in bootstrapValidate.
			bootstrapArgs.defaultComponents = bootstrapArgs.requiredComponents
			bootstrapArgs.gpgKeyRingPath = tt.gpgRing
			bootstrapArgs.gpgPassphrase = tt.gpgPass
			bootstrapArgs.sshSigningKeyFile = tt.sshKey
			bootstrapArgs.sshSigningPassword = tt.sshPass
			bootstrapArgs.sshSigningPassphrase = tt.sshPassp
			bootstrapArgs.privateKeyFile = tt.privateKey
			bootstrapArgs.sshSigningReusePrivateKey = tt.reuse

			err := bootstrapValidate()
			if tt.wantErr == "" {
				if err != nil {
					t.Fatalf("expected no error, got: %v", err)
				}
				return
			}
			if err == nil {
				t.Fatalf("expected error containing %q, got nil", tt.wantErr)
			}
			if !strings.Contains(err.Error(), tt.wantErr) {
				t.Fatalf("expected error containing %q, got: %v", tt.wantErr, err)
			}
		})
	}
}

// Providers that generate the SSH transport key in-process (github, gitea)
// must reject --ssh-signing-reuse-private-key with their own, provider-
// specific error before bootstrapValidate runs — otherwise the generic
// "--ssh-signing-reuse-private-key requires --private-key-file" error
// shadows the fact that the flag is fundamentally unsupported there.
func TestBootstrapProviderRejectsReuseBeforeValidate(t *testing.T) {
	tests := []struct {
		name    string
		runE    func() error
		wantErr string
	}{
		{
			name:    "github rejects reuse with provider-specific error",
			runE:    func() error { return bootstrapGitHubCmdRun(nil, nil) },
			wantErr: "not supported by 'bootstrap github'",
		},
		{
			name:    "gitea rejects reuse with provider-specific error",
			runE:    func() error { return bootstrapGiteaCmdRun(nil, nil) },
			wantErr: "not supported by 'bootstrap gitea'",
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			savedReuse := bootstrapArgs.sshSigningReusePrivateKey
			savedPrivKey := bootstrapArgs.privateKeyFile
			defer func() {
				bootstrapArgs.sshSigningReusePrivateKey = savedReuse
				bootstrapArgs.privateKeyFile = savedPrivKey
			}()

			// Reuse flag set, no --private-key-file: bootstrapValidate
			// would otherwise return "requires --private-key-file".
			bootstrapArgs.sshSigningReusePrivateKey = true
			bootstrapArgs.privateKeyFile = ""

			err := tt.runE()
			if err == nil {
				t.Fatalf("expected error containing %q, got nil", tt.wantErr)
			}
			if !strings.Contains(err.Error(), tt.wantErr) {
				t.Fatalf("expected error containing %q, got: %v", tt.wantErr, err)
			}
		})
	}
}
