// Copyright IBM Corp. 2020, 2026
// SPDX-License-Identifier: BUSL-1.1

package base_test

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"testing"

	"github.com/hashicorp/boundary/testing/internal/e2e"
	"github.com/hashicorp/boundary/testing/internal/e2e/boundary"
	"github.com/moby/moby/api/pkg/stdcopy"
	"github.com/moby/moby/client"
	"github.com/stretchr/testify/require"
)

// TestCliAuthenticateNoKeyring tests authentication when we're using `-format
// json` and there is no keyring on the system.
// This covers a case where the client cache daemon should intercept the auth
// token in the json response. There was previously a bug where the client cache
// daemon tried to access the keyring when there wasn't one on the system.
func TestCliAuthenticateNoKeyring(t *testing.T) {
	e2e.MaybeSkipTest(t)
	ctx := t.Context()

	bc, err := boundary.LoadConfig()
	require.NoError(t, err)

	containerID := "boundary"

	docker, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation())
	require.NoError(t, err)

	// Check that `pass` is not in the docker container
	execConfig := client.ExecCreateOptions{
		AttachStdout: true,
		AttachStderr: true,
		Cmd: []string{
			"which", "pass",
		},
	}
	exec, err := docker.ExecCreate(ctx, containerID, execConfig)
	require.NoError(t, err)
	resp, err := docker.ExecAttach(ctx, exec.ID, client.ExecAttachOptions{})
	require.NoError(t, err)
	t.Cleanup(func() {
		resp.Close()
	})

	var outBuf, errBuf bytes.Buffer
	outputDone := make(chan error)
	go func() {
		// StdCopy demultiplexes the stream into two buffers
		_, err = stdcopy.StdCopy(&outBuf, &errBuf, resp.Reader)
		outputDone <- err
	}()

	select {
	case err := <-outputDone:
		if err != nil {
			require.NoError(t, err)
		}
		break

	case <-ctx.Done():
		require.NoError(t, ctx.Err())
	}

	stdout, err := io.ReadAll(&outBuf)
	require.NoError(t, err)
	require.Empty(t, string(stdout))

	// Try to authenticate from inside the docker container
	execConfig = client.ExecCreateOptions{
		AttachStdout: true,
		AttachStderr: true,
		Cmd: []string{
			"boundary", "authenticate", "password",
			"-login-name", bc.AdminLoginName,
			"-password", "env://E2E_PASSWORD",
			"-format", "json",
		},
		Env: []string{
			fmt.Sprintf("BOUNDARY_ADDR=%s", bc.Address),
			fmt.Sprintf("E2E_PASSWORD=%s", bc.AdminLoginPassword),
		},
	}
	exec, err = docker.ExecCreate(ctx, containerID, execConfig)
	require.NoError(t, err)
	authResp, err := docker.ExecAttach(ctx, exec.ID, client.ExecAttachOptions{})
	require.NoError(t, err)
	t.Cleanup(func() {
		authResp.Close()
	})

	outBuf.Reset()
	errBuf.Reset()
	go func() {
		_, err = stdcopy.StdCopy(&outBuf, &errBuf, authResp.Reader)
		outputDone <- err
	}()

	select {
	case err := <-outputDone:
		if err != nil {
			require.NoError(t, err)
		}
		break

	case <-ctx.Done():
		require.NoError(t, ctx.Err())
	}

	stdout, err = io.ReadAll(&outBuf)
	require.NoError(t, err)
	require.NotEmpty(t, string(stdout))
	stderr, err := io.ReadAll(&errBuf)
	require.NoError(t, err)
	require.Empty(t, string(stderr))

	var authenticationResult boundary.AuthenticateCliOutput
	err = json.Unmarshal(stdout, &authenticationResult)
	require.NoError(t, err)
	require.Equal(t, http.StatusOK, authenticationResult.StatusCode, string(stdout))
	require.NotEmpty(t, authenticationResult.Item.Attributes["token"])
}
