// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1

package widmgr_test

import (
	"fmt"
	"testing"

	"github.com/hashicorp/nomad/ci"
	"github.com/hashicorp/nomad/client/widmgr"
	"github.com/hashicorp/nomad/command/agent"
	"github.com/hashicorp/nomad/helper/pointer"
	"github.com/hashicorp/nomad/helper/uuid"
	"github.com/hashicorp/nomad/nomad/mock"
	"github.com/hashicorp/nomad/nomad/structs"
	"github.com/hashicorp/nomad/testutil"
	"github.com/shoenig/test/must"
)

func TestWIDMgr(t *testing.T) {
	ci.Parallel(t)

	// Create a mixed ta
	ta := agent.NewTestAgent(t, "widtest", func(c *agent.Config) {
		c.Server.Enabled = true
		c.Server.NumSchedulers = pointer.Of(1)
		c.Client.Enabled = true
	})
	t.Cleanup(ta.Shutdown)

	mgr := widmgr.NewSigner(widmgr.SignerConfig{
		NodeSecret: ta.Client().Node().SecretID,
		Region:     "global",
		RPC:        ta,
	})

	_, err := mgr.SignIdentities(1, nil)
	must.ErrorContains(t, err, "no identities to sign")

	_, err = mgr.SignIdentities(1, []*structs.WorkloadIdentityRequest{
		{
			AllocID: uuid.Generate(),
			WIHandle: structs.WIHandle{
				WorkloadIdentifier: "web",
				IdentityName:       "foo",
			},
		},
	})
	must.ErrorContains(t, err, "rejected")

	// Register a job with 3 identities (but only 2 that need signing)
	job := mock.MinJob()
	job.TaskGroups[0].Tasks[0].Identity = &structs.WorkloadIdentity{
		Env: true,
	}
	job.TaskGroups[0].Tasks[0].Identities = []*structs.WorkloadIdentity{
		{
			Name:     "consul",
			Audience: []string{"a", "b"},
			Env:      true,
		},
		{
			Name: "vault",
			File: true,
		},
	}
	job.Canonicalize()

	testutil.RegisterJob(t, ta.RPC, job)

	var allocs []*structs.AllocListStub
	testutil.WaitForResult(func() (bool, error) {
		args := &structs.JobSpecificRequest{}
		args.JobID = job.ID
		args.QueryOptions.Region = job.Region
		args.Namespace = job.Namespace
		var resp structs.JobAllocationsResponse
		err := ta.RPC("Job.Allocations", args, &resp)
		if err != nil {
			return false, fmt.Errorf("Job.Allocations error: %v", err)
		}

		if len(resp.Allocations) == 0 {
			return false, fmt.Errorf("no allocs")
		}
		allocs = resp.Allocations
		return len(allocs) == 1, fmt.Errorf("unexpected number of allocs: %d", len(allocs))
	}, func(err error) {
		must.NoError(t, err)
	})
	must.Len(t, 1, allocs)

	// Get signed identites for alloc
	widreqs := []*structs.WorkloadIdentityRequest{
		{
			AllocID: allocs[0].ID,
			WIHandle: structs.WIHandle{
				WorkloadIdentifier: job.TaskGroups[0].Tasks[0].Name,
				IdentityName:       "consul",
			},
		},
		{
			AllocID: allocs[0].ID,
			WIHandle: structs.WIHandle{
				WorkloadIdentifier: job.TaskGroups[0].Tasks[0].Name,
				IdentityName:       "vault",
			},
		},
	}

	swids, err := mgr.SignIdentities(allocs[0].CreateIndex, widreqs)
	must.NoError(t, err)
	must.Len(t, 2, swids)
	must.Eq(t, *widreqs[0], swids[0].WorkloadIdentityRequest)
	must.StrContains(t, swids[0].JWT, ".")
	must.Eq(t, *widreqs[1], swids[1].WorkloadIdentityRequest)
	must.StrContains(t, swids[1].JWT, ".")
}
