package functions_test

import (
	"context"
	"testing"

	"github.com/google/go-cmp/cmp"

	fn "knative.dev/func/pkg/functions"
)

// TestRepository_TemplatesPath ensures that repositories can specify
// an alternate location for templates using a manifest.
func TestRepository_TemplatesPath(t *testing.T) {
	skipIfNoGit(t) // see docs
	client := fn.New(fn.WithRepositoriesPath("testdata/repositories"))

	// The repo ./testdata/repositories/customLanguagePackRepo includes a
	// manifest.yaml which defines templates as existing in the ./templates
	// directory within the repo.
	repo, err := client.Repositories().Get("customLanguagePackRepo")
	if err != nil {
		t.Fatal(err)
	}
	template, err := repo.Template("customRuntime", "customTemplate")
	if err != nil {
		t.Fatal(err)
	}
	// degenerate case: API of a custom repository should return what it was
	// expressly asked for at minimum (known good request)
	if template.Name() != "customTemplate" {
		t.Logf("expected custom language pack repo to yield a template named 'customTemplate', got '%v'", template.Name())
	}
}

// TestRepository_Inheritance ensures that repositories which define a manifest
// properly inherit values defined at the repo level, runtime level
// and template level.  The tests check for both embedded structures:
// HealthEndpoints BuildConfig.
func TestRepository_Inheritance(t *testing.T) {
	skipIfNoGit(t) // see docs
	var err error
	client := fn.New(fn.WithRepositoriesPath("testdata/repositories"))

	repo, err := client.Repositories().Get("customLanguagePackRepo")
	if err != nil {
		t.Fatal(err)
	}

	// Template A:  from a path containing no settings other than the repo root.
	// Should have a readiness and liveness equivalent to that defined in
	// [repo]/manifest.yaml
	fA := fn.Function{
		Name: "fn-a",
		Root: t.TempDir(),
	}
	tA, err := repo.Template("customRuntime", "customTemplate")
	if err != nil {
		t.Error(err)
	}
	err = tA.Write(context.Background(), &fA)
	if err != nil {
		t.Error(err)
	}

	// Template B: from a path containing runtime-wide settings, but no
	// template-level settings.
	fB := fn.Function{
		Name: "fn-b",
		Root: t.TempDir(),
	}
	tB, err := repo.Template("manifestedRuntime", "customTemplate")
	if err != nil {
		t.Error(err)
	}
	err = tB.Write(context.Background(), &fB)
	if err != nil {
		t.Error(err)
	}

	// Template C: from a runtime with a manifest which sets endpoints, and
	// itself includes a manifest which explicitly sets.
	fC := fn.Function{
		Name: "fn-c",
		Root: t.TempDir(),
	}
	tC, err := repo.Template("manifestedRuntime", "manifestedTemplate")
	if err != nil {
		t.Error(err)
	}
	err = tC.Write(context.Background(), &fC)
	if err != nil {
		t.Error(err)
	}

	// Assert Template A reflects repo-level settings
	if fA.Deploy.HealthEndpoints.Readiness != "/repoReadiness" {
		t.Errorf("Repository-level HealthEndpoint not loaded to template, got %q", fA.Deploy.HealthEndpoints.Readiness)
	}
	if diff := cmp.Diff([]string{"repoBuildpack"}, fA.Build.Buildpacks); diff != "" {
		t.Errorf("Repository-level Buildpack differs (-want, +got): %s", diff)
	}

	// Assert Template B reflects runtime-level settings
	if fB.Deploy.HealthEndpoints.Readiness != "/runtimeReadiness" {
		t.Errorf("Runtime-level HealthEndpoint not loaded to template, got %q", fB.Deploy.HealthEndpoints.Readiness)
	}
	if diff := cmp.Diff([]string{"runtimeBuildpack"}, fB.Build.Buildpacks); diff != "" {
		t.Errorf("Runtime-level Buildpack differs (-want, +got): %s", diff)
	}

	envVarName := "TEST_RUNTIME_VARIABLE"
	envVarValue := "test-runtime"
	envs := []fn.Env{
		{
			Name:  &envVarName,
			Value: &envVarValue,
		},
	}

	if diff := cmp.Diff(fn.Envs(envs), fB.Build.BuildEnvs); diff != "" {
		t.Fatalf("Unexpected difference between repository's manifest.yaml buildEnvs and function BuildEnvs (-want, +got): %v", diff)
	}

	// Assert Template C reflects template-level settings
	if fC.Deploy.HealthEndpoints.Readiness != "/templateReadiness" {
		t.Fatalf("Template-level HealthEndpoint not loaded to template, got %q", fC.Deploy.HealthEndpoints.Readiness)
	}
	if diff := cmp.Diff([]string{"templateBuildpack"}, fC.Build.Buildpacks); diff != "" {
		t.Fatalf("Template-level Buildpack differs (-want, +got): %s", diff)
	}
}
