package testsuite

import (
	"context"
	"testing"

	"forge.cadoles.com/Cadoles/emissary/internal/agent/controller/mdns/spec"
	"forge.cadoles.com/Cadoles/emissary/internal/datastore"
	"forge.cadoles.com/Cadoles/emissary/internal/jwk"
	"github.com/pkg/errors"
)

type agentRepositoryTestCase struct {
	Name string
	Skip bool
	Run  func(ctx context.Context, repo datastore.AgentRepository) error
}

var agentRepositoryTestCases = []agentRepositoryTestCase{
	{
		Name: "Create a new agent",
		Run: func(ctx context.Context, repo datastore.AgentRepository) error {
			thumbprint := "foo"
			keySet := jwk.NewSet()
			var metadata map[string]any

			agent, err := repo.Create(ctx, thumbprint, keySet, metadata)
			if err != nil {
				return errors.WithStack(err)
			}

			if agent.CreatedAt.IsZero() {
				return errors.Errorf("agent.CreatedAt should not be zero time")
			}

			if agent.UpdatedAt.IsZero() {
				return errors.Errorf("agent.UpdatedAt should not be zero time")
			}

			if e, g := datastore.AgentStatusPending, agent.Status; e != g {
				return errors.Errorf("agent.Status: expected '%v', got '%v'", e, g)
			}

			return nil
		},
	},
	{
		Name: "Try to update spec for an unexistant agent",
		Run: func(ctx context.Context, repo datastore.AgentRepository) error {
			var unexistantAgentID datastore.AgentID = 9999
			var specData map[string]any

			agent, err := repo.UpdateSpec(ctx, unexistantAgentID, string(spec.Name), 0, specData)
			if err == nil {
				return errors.New("error should not be nil")
			}

			if !errors.Is(err, datastore.ErrNotFound) {
				return errors.Errorf("error should be datastore.ErrNotFound, got '%+v'", err)
			}

			if agent != nil {
				return errors.New("agent should be nil")
			}

			return nil
		},
	},
	{
		Name: "Try to delete spec of an unexistant agent",
		Run: func(ctx context.Context, repo datastore.AgentRepository) error {
			var unexistantAgentID datastore.AgentID = 9999

			err := repo.DeleteSpec(ctx, unexistantAgentID, string(spec.Name))
			if err == nil {
				return errors.New("error should not be nil")
			}

			if !errors.Is(err, datastore.ErrNotFound) {
				return errors.Errorf("error should be datastore.ErrNotFound, got '%+v'", err)
			}

			return nil
		},
	},
	{
		Name: "Try to get specs of an unexistant agent",
		Run: func(ctx context.Context, repo datastore.AgentRepository) error {
			var unexistantAgentID datastore.AgentID = 9999

			specs, err := repo.GetSpecs(ctx, unexistantAgentID)
			if err == nil {
				return errors.New("error should not be nil")
			}

			if !errors.Is(err, datastore.ErrNotFound) {
				return errors.Errorf("error should be datastore.ErrNotFound, got '%+v'", err)
			}

			if specs != nil {
				return errors.Errorf("specs should be nil, got '%+v'", err)
			}

			return nil
		},
	},
}

func runAgentRepositoryTests(t *testing.T, repo datastore.AgentRepository) {
	for _, tc := range agentRepositoryTestCases {
		func(tc agentRepositoryTestCase) {
			t.Run(tc.Name, func(t *testing.T) {
				t.Parallel()

				if tc.Skip {
					t.SkipNow()

					return
				}

				ctx := context.Background()

				if err := tc.Run(ctx, repo); err != nil {
					t.Errorf("%+v", errors.WithStack(err))
				}
			})
		}(tc)
	}
}