package service

import (
	"context"
	"errors"
	"testing"
	"time"

	persistencemock "github.com/ocdp/cluster-service/internal/adapter/output/persistence/mock"
	"github.com/ocdp/cluster-service/internal/domain/entity"
	"github.com/ocdp/cluster-service/internal/domain/repository"
	"github.com/ocdp/cluster-service/internal/pkg/authz"
)

func TestDeleteInstanceIgnoresMissingRelease(t *testing.T) {
	principal := &authz.Principal{UserID: "user-1", Username: "tester", Role: authz.RoleUser, WorkspaceID: entity.DefaultWorkspaceID}
	ctx := authz.WithPrincipal(context.Background(), principal)
	instanceRepo := persistencemock.NewInstanceRepositoryMock()

	instance := &entity.Instance{
		ID:          "inst-1",
		WorkspaceID: entity.DefaultWorkspaceID,
		OwnerID:     "user-1",
		ClusterID:   "cluster-1",
		Name:        "demo",
		Namespace:   "default",
	}
	if err := instanceRepo.Create(ctx, instance); err != nil {
		t.Fatalf("failed to seed instance: %v", err)
	}

	cluster := &entity.Cluster{ID: "cluster-1", Name: "cluster", Host: "https://example.com"}
	clusterRepo := &stubClusterRepo{cluster: cluster}

	svc := NewInstanceService(
		instanceRepo,
		clusterRepo,
		nil,
		&stubHelmClient{uninstallErr: entity.ErrInstanceNotFound},
		nil,
		nil,
	)

	if err := svc.DeleteInstance(ctx, instance.ID); err != nil {
		t.Fatalf("DeleteInstance returned error: %v", err)
	}

	waitForInstanceDeleted(t, ctx, instanceRepo, instance.ID)
}

func TestEnforceNamespaceValuesOverridesChartNamespaceKnobs(t *testing.T) {
	instance := &entity.Instance{
		Namespace: "ocdp-u-alice",
		Values: map[string]interface{}{
			"namespace":         "default",
			"namespaceOverride": "default",
			"targetNamespace":   "default",
			"global": map[string]interface{}{
				"namespace":         "default",
				"namespaceOverride": "default",
			},
			"image": map[string]interface{}{
				"repository": "nginx",
			},
		},
	}

	enforceNamespaceValues(instance)

	if instance.Values["namespace"] != "ocdp-u-alice" {
		t.Fatalf("expected top-level namespace to be enforced, got %#v", instance.Values["namespace"])
	}
	if instance.Values["namespaceOverride"] != "ocdp-u-alice" {
		t.Fatalf("expected namespaceOverride to be enforced, got %#v", instance.Values["namespaceOverride"])
	}
	if instance.Values["targetNamespace"] != "ocdp-u-alice" {
		t.Fatalf("expected targetNamespace to be enforced, got %#v", instance.Values["targetNamespace"])
	}
	global, ok := instance.Values["global"].(map[string]interface{})
	if !ok {
		t.Fatalf("expected global map, got %#v", instance.Values["global"])
	}
	if global["namespace"] != "ocdp-u-alice" || global["namespaceOverride"] != "ocdp-u-alice" {
		t.Fatalf("expected global namespace keys to be enforced, got %#v", global)
	}
}

func waitForInstanceDeleted(t *testing.T, ctx context.Context, repo repository.InstanceRepository, id string) {
	t.Helper()

	deadline := time.After(2 * time.Second)
	ticker := time.NewTicker(10 * time.Millisecond)
	defer ticker.Stop()

	for {
		select {
		case <-deadline:
			_, err := repo.GetByID(ctx, id)
			t.Fatalf("expected instance removed, got err=%v", err)
		case <-ticker.C:
			if _, err := repo.GetByID(ctx, id); errors.Is(err, entity.ErrInstanceNotFound) {
				return
			}
		}
	}
}

type stubClusterRepo struct {
	cluster *entity.Cluster
}

func (s *stubClusterRepo) Create(ctx context.Context, cluster *entity.Cluster) error {
	s.cluster = cluster
	return nil
}

func (s *stubClusterRepo) GetByID(ctx context.Context, id string) (*entity.Cluster, error) {
	if s.cluster != nil && s.cluster.ID == id {
		return s.cluster, nil
	}
	return nil, entity.ErrClusterNotFound
}

func (*stubClusterRepo) GetByName(ctx context.Context, name string) (*entity.Cluster, error) {
	return nil, entity.ErrClusterNotFound
}

func (*stubClusterRepo) Update(ctx context.Context, cluster *entity.Cluster) error { return nil }

func (*stubClusterRepo) Delete(ctx context.Context, id string) error { return nil }

func (*stubClusterRepo) List(ctx context.Context) ([]*entity.Cluster, error) { return nil, nil }

type stubHelmClient struct {
	uninstallErr error
}

func (*stubHelmClient) Install(ctx context.Context, cluster *entity.Cluster, instance *entity.Instance) error {
	return nil
}

func (*stubHelmClient) Upgrade(ctx context.Context, cluster *entity.Cluster, instance *entity.Instance) error {
	return nil
}

func (s *stubHelmClient) Uninstall(ctx context.Context, cluster *entity.Cluster, releaseName, namespace string) error {
	return s.uninstallErr
}

func (*stubHelmClient) Rollback(ctx context.Context, cluster *entity.Cluster, releaseName, namespace string, revision int) error {
	return nil
}

func (*stubHelmClient) GetStatus(ctx context.Context, cluster *entity.Cluster, releaseName, namespace string) (*entity.Instance, error) {
	return nil, nil
}

func (*stubHelmClient) GetHistory(ctx context.Context, cluster *entity.Cluster, releaseName, namespace string) ([]*entity.ReleaseHistory, error) {
	return nil, nil
}

func (*stubHelmClient) List(ctx context.Context, cluster *entity.Cluster, namespace string) ([]*entity.Instance, error) {
	return nil, nil
}

func (*stubHelmClient) GetValues(ctx context.Context, cluster *entity.Cluster, releaseName, namespace string) (map[string]interface{}, error) {
	return nil, nil
}

func (*stubHelmClient) GetChartDefaultValues(chartPath string) (map[string]interface{}, error) {
	return nil, nil
}

var _ repository.ClusterRepository = (*stubClusterRepo)(nil)
var _ repository.HelmClient = (*stubHelmClient)(nil)
