package adapterimpl

import (
	"context"
	"encoding/json"
	"errors"
	"net/http"
	"net/http/httptest"
	"strings"
	"testing"
	"time"

	"github.com/onsi/gomega"

	globalcommonv1 "github.com/tidbcloud/tidb-management-service/api/spec/global/common/v1"
	"github.com/tidbcloud/tidb-management-service/serverless-global-service/domain/adapter"
	"github.com/tidbcloud/tidb-management-service/serverless-global-service/domain/entity"
)

type fakeSys9AuthManager struct {
	session *adapter.Sys9Session
	err     error
}

func (f *fakeSys9AuthManager) GetSession(_ context.Context, _ *entity.Region) (*adapter.Sys9Session, error) {
	if f.err != nil {
		return nil, f.err
	}
	return f.session, nil
}

func (f *fakeSys9AuthManager) Invalidate(context.Context, *entity.Region) {}

type fakeSys9OEMProfileProvider struct {
	profile *adapter.Sys9OEMProfile
	err     error
}

func (f *fakeSys9OEMProfileProvider) ResolveProfile(_ context.Context, _ string) (*adapter.Sys9OEMProfile, error) {
	if f.err != nil {
		return nil, f.err
	}
	return f.profile, nil
}

func TestSys9LifecycleDeleteClusterWithDevToken(t *testing.T) {
	g := gomega.NewWithT(t)

	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		g.Expect(r.Method).Should(gomega.Equal(http.MethodDelete))
		g.Expect(r.URL.Path).Should(gomega.Equal("/api/v1/service-instances/inst-1"))
		g.Expect(r.Header.Get("Authorization")).Should(gomega.Equal("Bearer mock-dev-token"))
		g.Expect(r.Header.Get("DPoP")).Should(gomega.Equal("mock-dpop-proof"))
		w.WriteHeader(http.StatusAccepted)
	}))
	defer server.Close()

	lifecycle := NewSys9Lifecycle(
		&fakeSys9AuthManager{session: &adapter.Sys9Session{AccessToken: "session-token", SessionToken: "session-dpop"}},
		&fakeSys9OEMProfileProvider{profile: &adapter.Sys9OEMProfile{
			DevToken:  "mock-dev-token",
			DPoPProof: "mock-dpop-proof",
		}},
	)

	err := lifecycle.DeleteCluster(t.Context(), &entity.Cluster{Region: &entity.Region{
		ServiceAddress: server.URL,
		Sys9Config:     &globalcommonv1.RegionSys9Config{OemProfileRef: "profile/ref"},
	}}, "inst-1")
	g.Expect(err).ShouldNot(gomega.HaveOccurred())
}

func TestSys9LifecycleDeleteClusterRequiresServiceInstanceID(t *testing.T) {
	g := gomega.NewWithT(t)

	lifecycle := NewSys9Lifecycle(
		&fakeSys9AuthManager{session: &adapter.Sys9Session{AccessToken: "session-token", SessionToken: "session-dpop"}},
		&fakeSys9OEMProfileProvider{profile: &adapter.Sys9OEMProfile{}},
	)

	err := lifecycle.DeleteCluster(t.Context(), &entity.Cluster{Region: &entity.Region{ServiceAddress: "http://127.0.0.1"}}, "")
	g.Expect(err).Should(gomega.HaveOccurred())
	g.Expect(err.Error()).Should(gomega.ContainSubstring("service instance id is required"))
}

func TestSys9LifecycleDeleteClusterUsesSessionTokenWhenNoDevToken(t *testing.T) {
	g := gomega.NewWithT(t)

	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		g.Expect(r.Header.Get("Authorization")).Should(gomega.Equal("Bearer session-token"))
		g.Expect(r.Header.Get("DPoP")).Should(gomega.Equal("session-dpop"))
		w.WriteHeader(http.StatusAccepted)
	}))
	defer server.Close()

	lifecycle := NewSys9Lifecycle(
		&fakeSys9AuthManager{session: &adapter.Sys9Session{AccessToken: "session-token", SessionToken: "session-dpop"}},
		&fakeSys9OEMProfileProvider{profile: &adapter.Sys9OEMProfile{}},
	)

	err := lifecycle.DeleteCluster(t.Context(), &entity.Cluster{Region: &entity.Region{
		ServiceAddress: server.URL,
		Sys9Config:     &globalcommonv1.RegionSys9Config{OemProfileRef: "profile/ref"},
	}}, "inst-2")
	g.Expect(err).ShouldNot(gomega.HaveOccurred())
}

func TestSys9LifecycleDeleteClusterAuthSessionError(t *testing.T) {
	g := gomega.NewWithT(t)

	lifecycle := NewSys9Lifecycle(
		&fakeSys9AuthManager{err: errors.New("get session failed")},
		&fakeSys9OEMProfileProvider{profile: &adapter.Sys9OEMProfile{}},
	)

	err := lifecycle.DeleteCluster(t.Context(), &entity.Cluster{Region: &entity.Region{
		ServiceAddress: "http://127.0.0.1",
		Sys9Config:     &globalcommonv1.RegionSys9Config{OemProfileRef: "profile/ref"},
	}}, "inst-3")
	g.Expect(err).Should(gomega.HaveOccurred())
	g.Expect(err.Error()).Should(gomega.ContainSubstring("get session failed"))
}

func TestSys9LifecycleRedeemManagedDatabaseAdministrativeCredentialWithSecretEnvelope(t *testing.T) {
	g := gomega.NewWithT(t)

	const (
		instanceID = "395f19b0-142d-45f0-9dc6-609ef94348df"
	)

	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		switch {
		case r.Method == http.MethodGet && r.URL.Path == "/api/v1/service-instances/"+instanceID:
			w.Header().Set("Content-Type", "application/json")
			w.Header().Set("ETag", "\"etag-123\"")
			_, _ = w.Write([]byte(`{"generation":2,"managedDatabase":{"administrativeCredentialGeneration":3}}`))
			return
		case r.Method == http.MethodGet && r.URL.Path == "/api/v1/service-instances/"+instanceID+"/managed-database":
			w.Header().Set("Content-Type", "application/json")
			w.Header().Set("ETag", "\"md-etag-1\"")
			_, _ = w.Write([]byte(`{"version":3}`))
			return
		case r.Method == http.MethodPost && r.URL.Path == "/api/v1/secret-delivery-challenges":
			g.Expect(r.Header.Get("X-Request-Context-ID")).ShouldNot(gomega.BeEmpty())
			var body map[string]any
			err := json.NewDecoder(r.Body).Decode(&body)
			g.Expect(err).ShouldNot(gomega.HaveOccurred())
			scope, ok := body["scope"].(map[string]any)
			g.Expect(ok).Should(gomega.BeTrue())
			g.Expect(scope["serviceInstanceId"]).Should(gomega.Equal(instanceID))
			g.Expect(scope["serviceInstanceGeneration"]).Should(gomega.Equal(float64(2)))
			g.Expect(scope["credentialGeneration"]).Should(gomega.Equal(float64(3)))
			g.Expect(scope["credentialKind"]).Should(gomega.Equal("SECRET_CREDENTIAL_KIND_MANAGED_DATABASE_ADMINISTRATIVE"))
			g.Expect(scope["plane"]).Should(gomega.Equal("PLANE_DB"))
			g.Expect(body["clientPublicKey"]).Should(gomega.Equal("dGVzdC1wdWJrZXk="))
			g.Expect(body["tlsExporterSha256"]).Should(gomega.Equal("dGVzdC10bHMtZGlnZXN0"))

			w.Header().Set("Content-Type", "application/json")
			_, _ = w.Write([]byte(`{"challengeId":"11111111-1111-4111-8111-111111111111","challenge":"Q0hBTExFTkdFUFJPT0Y="}`))
			return
		case r.Method == http.MethodPost && r.URL.Path == "/api/v1/secret-delivery-challenges:redeem":
			g.Expect(r.Header.Get("X-Request-Context-ID")).ShouldNot(gomega.BeEmpty())
			var body map[string]any
			err := json.NewDecoder(r.Body).Decode(&body)
			g.Expect(err).ShouldNot(gomega.HaveOccurred())
			g.Expect(body["challengeId"]).Should(gomega.Equal("11111111-1111-4111-8111-111111111111"))
			g.Expect(body["challengeProof"]).Should(gomega.Equal("Q0hBTExFTkdFUFJPT0Y="))
			scope, ok := body["scope"].(map[string]any)
			g.Expect(ok).Should(gomega.BeTrue())
			g.Expect(scope["serviceInstanceId"]).Should(gomega.Equal(instanceID))
			g.Expect(scope["serviceInstanceGeneration"]).Should(gomega.Equal(float64(2)))
			g.Expect(scope["credentialGeneration"]).Should(gomega.Equal(float64(3)))
			g.Expect(scope["credentialKind"]).Should(gomega.Equal("SECRET_CREDENTIAL_KIND_MANAGED_DATABASE_ADMINISTRATIVE"))
			g.Expect(scope["plane"]).Should(gomega.Equal("PLANE_DB"))
			g.Expect(body["clientPublicKey"]).Should(gomega.Equal("dGVzdC1wdWJrZXk="))
			g.Expect(body["tlsExporterSha256"]).Should(gomega.Equal("dGVzdC10bHMtZGlnZXN0"))

			w.Header().Set("Content-Type", "application/json")
			_, _ = w.Write(
				[]byte(
					`{"secretDeliveryId":"22222222-2222-4222-8222-222222222222","sealedCredentialVersion":"1","secretReferenceSha256":"3Dw/MJ2F+R3RN+w+gnmL4pVbofIWss5mjC7WOm7+vvs=","keyEncapsulationAlgorithm":"MOCK","encapsulatedKey":"AAAAAAAAAAAAAAAAAAAAAA==","contentEncryptionAlgorithm":"MOCK","nonce":"AAAAAAAAAAAAAAAAAAAAAA==","ciphertext":"AAAAAAAAAAAAAAAAAAAAAA==","associatedDataSha256":"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=","clientPublicKeySha256":"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=","issuedAt":"2025-01-01T00:00:00Z","deliveryWindowExpiresAt":"2030-01-01T00:00:00Z","issuerSignature":"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="}`,
				),
			)
			return
		default:
			t.Fatalf("unexpected request: %s %s", r.Method, r.URL.Path)
		}
	}))
	defer server.Close()

	lifecycle := NewSys9Lifecycle(
		&fakeSys9AuthManager{session: &adapter.Sys9Session{AccessToken: "session-token", SessionToken: "session-dpop"}},
		&fakeSys9OEMProfileProvider{profile: &adapter.Sys9OEMProfile{
			DevToken:               "mock-dev-token",
			DPoPProof:              "mock-dpop-proof",
			DPoPPublicKey:          "dGVzdC1wdWJrZXk=",
			TemporaryDPoPPublicKey: "dGVzdC1wdWJrZXk=",
			TLSExporterDigest:      "dGVzdC10bHMtZGlnZXN0",
		}},
	)

	password, err := lifecycle.RedeemManagedDatabaseAdministrativeCredential(t.Context(), &entity.Cluster{Region: &entity.Region{
		ServiceAddress: server.URL,
		Sys9Config:     &globalcommonv1.RegionSys9Config{OemProfileRef: "profile/ref"},
	}}, instanceID, "")
	g.Expect(err).ShouldNot(gomega.HaveOccurred())
	g.Expect(password).Should(gomega.Equal("mock-secret"))
}

func TestSys9LifecycleRotateManagedDatabaseAdministrativeCredentialSetsIfMatch(t *testing.T) {
	g := gomega.NewWithT(t)

	const (
		instanceID = "395f19b0-142d-45f0-9dc6-609ef94348df"
	)

	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		switch {
		case r.Method == http.MethodGet && r.URL.Path == "/api/v1/service-instances/"+instanceID:
			w.Header().Set("Content-Type", "application/json")
			w.Header().Set("ETag", "\"etag-123\"")
			_, _ = w.Write([]byte(`{"generation":2,"managedDatabase":{"administrativeCredentialGeneration":3}}`))
			return
		case r.Method == http.MethodPost && r.URL.Path == "/api/v1/service-instances/"+instanceID+"/managed-database:rotate-administrative-credential":
			g.Expect(r.Header.Get("X-Request-Context-ID")).ShouldNot(gomega.BeEmpty())
			g.Expect(r.Header.Get("If-Match")).Should(gomega.Equal("\"etag-123\""))
			g.Expect(r.Header.Get("Idempotency-Key")).Should(gomega.Equal("op-token-1"))
			w.Header().Set("Content-Type", "application/json")
			_, _ = w.Write([]byte(`{"operationId":"op-1"}`))
			return
		default:
			t.Fatalf("unexpected request: %s %s", r.Method, r.URL.Path)
		}
	}))
	defer server.Close()

	lifecycle := NewSys9Lifecycle(
		&fakeSys9AuthManager{session: &adapter.Sys9Session{AccessToken: "session-token", SessionToken: "session-dpop"}},
		&fakeSys9OEMProfileProvider{profile: &adapter.Sys9OEMProfile{
			DevToken:  "mock-dev-token",
			DPoPProof: "mock-dpop-proof",
		}},
	)

	opID, err := lifecycle.RotateManagedDatabaseAdministrativeCredential(t.Context(), &entity.Cluster{Region: &entity.Region{
		ServiceAddress: server.URL,
		Sys9Config:     &globalcommonv1.RegionSys9Config{OemProfileRef: "profile/ref"},
	}}, instanceID, "op-token-1")
	g.Expect(err).ShouldNot(gomega.HaveOccurred())
	g.Expect(opID).Should(gomega.Equal("op-1"))
}

func TestSys9LifecycleRotateManagedDatabaseAdministrativeCredentialRequiresETag(t *testing.T) {
	g := gomega.NewWithT(t)

	const instanceID = "395f19b0-142d-45f0-9dc6-609ef94348df"

	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if r.Method == http.MethodGet && r.URL.Path == "/api/v1/service-instances/"+instanceID {
			w.Header().Set("Content-Type", "application/json")
			_, _ = w.Write([]byte(`{"generation":2,"managedDatabase":{"administrativeCredentialGeneration":3}}`))
			return
		}
		t.Fatalf("unexpected request: %s %s", r.Method, r.URL.Path)
	}))
	defer server.Close()

	lifecycle := NewSys9Lifecycle(
		&fakeSys9AuthManager{session: &adapter.Sys9Session{AccessToken: "session-token", SessionToken: "session-dpop"}},
		&fakeSys9OEMProfileProvider{profile: &adapter.Sys9OEMProfile{}},
	)

	_, err := lifecycle.RotateManagedDatabaseAdministrativeCredential(t.Context(), &entity.Cluster{Region: &entity.Region{
		ServiceAddress: server.URL,
		Sys9Config:     &globalcommonv1.RegionSys9Config{OemProfileRef: "profile/ref"},
	}}, instanceID, "op-token-1")
	g.Expect(err).Should(gomega.HaveOccurred())
	g.Expect(strings.Contains(err.Error(), "etag is missing")).Should(gomega.BeTrue())
}

func TestSys9LifecycleRotateManagedDatabaseAdministrativeCredentialParsesStringGeneration(t *testing.T) {
	g := gomega.NewWithT(t)

	const instanceID = "395f19b0-142d-45f0-9dc6-609ef94348df"

	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		switch {
		case r.Method == http.MethodGet && r.URL.Path == "/api/v1/service-instances/"+instanceID:
			w.Header().Set("Content-Type", "application/json")
			_, _ = w.Write([]byte(`{"generation":"1","strongEtag":"etag-from-body","managedDatabase":{"administrativeCredentialGeneration":"3"}}`))
			return
		case r.Method == http.MethodGet && r.URL.Path == "/api/v1/service-instances/"+instanceID+"/managed-database":
			w.Header().Set("Content-Type", "application/json")
			_, _ = w.Write([]byte(`{"version":"3","strongEtag":"md-etag-from-body"}`))
			return
		case r.Method == http.MethodPost && r.URL.Path == "/api/v1/service-instances/"+instanceID+"/managed-database:rotate-administrative-credential":
			g.Expect(r.Header.Get("X-Request-Context-ID")).ShouldNot(gomega.BeEmpty())
			g.Expect(r.Header.Get("If-Match")).Should(gomega.Equal("etag-from-body"))
			w.Header().Set("Content-Type", "application/json")
			_, _ = w.Write([]byte(`{"operationId":"op-1"}`))
			return
		default:
			t.Fatalf("unexpected request: %s %s", r.Method, r.URL.Path)
		}
	}))
	defer server.Close()

	lifecycle := NewSys9Lifecycle(
		&fakeSys9AuthManager{session: &adapter.Sys9Session{AccessToken: "session-token", SessionToken: "session-dpop"}},
		&fakeSys9OEMProfileProvider{profile: &adapter.Sys9OEMProfile{}},
	)

	_, err := lifecycle.RotateManagedDatabaseAdministrativeCredential(t.Context(), &entity.Cluster{Region: &entity.Region{
		ServiceAddress: server.URL,
		Sys9Config:     &globalcommonv1.RegionSys9Config{OemProfileRef: "profile/ref"},
	}}, instanceID, "op-token-1")
	g.Expect(err).ShouldNot(gomega.HaveOccurred())
}

func TestSys9LifecycleListManagedDatabaseBackupsWithFilter(t *testing.T) {
	g := gomega.NewWithT(t)

	const instanceID = "e6291902-f493-443f-b16d-1729ed2c75a9"
	lastBackupTime := time.Date(2026, 9, 4, 8, 0, 0, 0, time.UTC)

	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		g.Expect(r.Method).Should(gomega.Equal(http.MethodGet))
		g.Expect(r.URL.Path).Should(gomega.Equal("/api/v1/service-instances/" + instanceID + "/managed-database/backups"))
		g.Expect(r.URL.Query().Get("pageSize")).Should(gomega.Equal("2"))
		g.Expect(r.URL.Query().Get("pageToken")).Should(gomega.Equal("token-1"))
		g.Expect(r.Header.Get("Authorization")).Should(gomega.Equal("Bearer mock-dev-token"))
		g.Expect(r.Header.Get("DPoP")).Should(gomega.Equal("mock-dpop-proof"))
		w.Header().Set("Content-Type", "application/json")
		_, _ = w.Write(
			[]byte(
				`{"backups":[{"backupId":"11111111-1111-4111-8111-111111111111","createdAt":"2026-09-04T07:00:00Z","expiresAt":"2026-09-11T07:00:00Z","state":"BACKUP_STATE_AVAILABLE"},{"backupId":"22222222-2222-4222-8222-222222222222","createdAt":"2026-09-04T09:00:00Z","expiresAt":"2026-09-11T09:00:00Z","state":"BACKUP_STATE_AVAILABLE"}],"nextPageToken":"token-2"}`,
			),
		)
	}))
	defer server.Close()

	lifecycle := NewSys9Lifecycle(
		&fakeSys9AuthManager{session: &adapter.Sys9Session{AccessToken: "session-token", SessionToken: "session-dpop"}},
		&fakeSys9OEMProfileProvider{profile: &adapter.Sys9OEMProfile{DevToken: "mock-dev-token", DPoPProof: "mock-dpop-proof"}},
	)

	resp, err := lifecycle.ListManagedDatabaseBackups(t.Context(), &entity.Cluster{Region: &entity.Region{
		ServiceAddress: server.URL,
		Sys9Config:     &globalcommonv1.RegionSys9Config{OemProfileRef: "profile/ref"},
	}}, instanceID, &adapter.Sys9ListManagedDatabaseBackupsRequest{
		LastBackupTime: &lastBackupTime,
		MaxCount:       2,
		PageToken:      "token-1",
	})
	g.Expect(err).ShouldNot(gomega.HaveOccurred())
	g.Expect(resp.HasMore).Should(gomega.BeTrue())
	g.Expect(resp.NextPageToken).Should(gomega.Equal("token-2"))
	g.Expect(resp.Items).Should(gomega.HaveLen(1))
	g.Expect(resp.Items[0].BackupID).Should(gomega.Equal("22222222-2222-4222-8222-222222222222"))
	g.Expect(resp.Items[0].BackupTime.Equal(time.Date(2026, 9, 4, 9, 0, 0, 0, time.UTC))).Should(gomega.BeTrue())
	g.Expect(resp.Items[0].Expiration).ShouldNot(gomega.BeNil())
}

func TestSys9LifecycleCreateManagedDatabaseBackup(t *testing.T) {
	g := gomega.NewWithT(t)

	const instanceID = "e6291902-f493-443f-b16d-1729ed2c75a9"

	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		switch {
		case r.Method == http.MethodGet && r.URL.Path == "/api/v1/service-instances/"+instanceID:
			w.Header().Set("Content-Type", "application/json")
			w.Header().Set("ETag", "\"instance-etag\"")
			_, _ = w.Write([]byte(`{"generation":2}`))
			return
		case r.Method == http.MethodPost && r.URL.Path == "/api/v1/service-instances/"+instanceID+"/managed-database/backups":
			g.Expect(r.Header.Get("If-Match")).Should(gomega.Equal("\"instance-etag\""))
			g.Expect(r.Header.Get("Idempotency-Key")).ShouldNot(gomega.BeEmpty())
			g.Expect(r.Header.Get("X-Request-Context-ID")).ShouldNot(gomega.BeEmpty())
			var body map[string]any
			err := json.NewDecoder(r.Body).Decode(&body)
			g.Expect(err).ShouldNot(gomega.HaveOccurred())
			g.Expect(body["type"]).Should(gomega.Equal("BACKUP_TYPE_ON_DEMAND"))
			w.Header().Set("Content-Type", "application/json")
			w.WriteHeader(http.StatusAccepted)
			_, _ = w.Write([]byte(`{"operationId":"backup-op-1"}`))
			return
		default:
			t.Fatalf("unexpected request: %s %s", r.Method, r.URL.Path)
		}
	}))
	defer server.Close()

	lifecycle := NewSys9Lifecycle(
		&fakeSys9AuthManager{session: &adapter.Sys9Session{AccessToken: "session-token", SessionToken: "session-dpop"}},
		&fakeSys9OEMProfileProvider{profile: &adapter.Sys9OEMProfile{DevToken: "mock-dev-token", DPoPProof: "mock-dpop-proof"}},
	)

	operationID, err := lifecycle.CreateManagedDatabaseBackup(t.Context(), &entity.Cluster{Region: &entity.Region{
		ServiceAddress: server.URL,
		Sys9Config:     &globalcommonv1.RegionSys9Config{OemProfileRef: "profile/ref"},
	}}, instanceID)
	g.Expect(err).ShouldNot(gomega.HaveOccurred())
	g.Expect(operationID).Should(gomega.Equal("backup-op-1"))
}

func TestSys9LifecycleGetManagedDatabaseBackup(t *testing.T) {
	g := gomega.NewWithT(t)

	const (
		instanceID = "e6291902-f493-443f-b16d-1729ed2c75a9"
		backupID   = "11111111-1111-4111-8111-111111111111"
	)

	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		g.Expect(r.Method).Should(gomega.Equal(http.MethodGet))
		g.Expect(r.URL.Path).Should(gomega.Equal("/api/v1/service-instances/" + instanceID + "/managed-database/backups/" + backupID))
		w.Header().Set("Content-Type", "application/json")
		_, _ = w.Write(
			[]byte(
				`{"backupId":"11111111-1111-4111-8111-111111111111","createdAt":"2026-09-04T09:00:00Z","expiresAt":"2026-09-11T09:00:00Z","state":"BACKUP_STATE_AVAILABLE"}`,
			),
		)
	}))
	defer server.Close()

	lifecycle := NewSys9Lifecycle(
		&fakeSys9AuthManager{session: &adapter.Sys9Session{AccessToken: "session-token", SessionToken: "session-dpop"}},
		&fakeSys9OEMProfileProvider{profile: &adapter.Sys9OEMProfile{DevToken: "mock-dev-token", DPoPProof: "mock-dpop-proof"}},
	)

	resp, err := lifecycle.GetManagedDatabaseBackup(t.Context(), &entity.Cluster{Region: &entity.Region{
		ServiceAddress: server.URL,
		Sys9Config:     &globalcommonv1.RegionSys9Config{OemProfileRef: "profile/ref"},
	}}, instanceID, backupID)
	g.Expect(err).ShouldNot(gomega.HaveOccurred())
	g.Expect(resp.BackupID).Should(gomega.Equal(backupID))
	g.Expect(resp.BackupName).Should(gomega.Equal(backupID))
	g.Expect(resp.State).Should(gomega.Equal("BACKUP_STATE_AVAILABLE"))
}

func TestSys9LifecycleGetAndUpdateManagedDatabaseBackupPolicy(t *testing.T) {
	g := gomega.NewWithT(t)

	const instanceID = "e6291902-f493-443f-b16d-1729ed2c75a9"

	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		switch {
		case r.Method == http.MethodGet && r.URL.Path == "/api/v1/service-instances/"+instanceID+"/managed-database/backup-policy":
			w.Header().Set("Content-Type", "application/json")
			w.Header().Set("ETag", "\"policy-etag\"")
			_, _ = w.Write(
				[]byte(
					`{"enabled":true,"retentionDays":7,"schedule":"BACKUP_SCHEDULE_DAILY","serviceInstanceId":"e6291902-f493-443f-b16d-1729ed2c75a9","strongEtag":"cG9saWN5LWV0YWc=","version":"1"}`,
				),
			)
			return
		case r.Method == http.MethodPatch && r.URL.Path == "/api/v1/service-instances/"+instanceID+"/managed-database/backup-policy":
			g.Expect(r.Header.Get("If-Match")).Should(gomega.Equal("\"policy-etag\""))
			g.Expect(r.Header.Get("Idempotency-Key")).ShouldNot(gomega.BeEmpty())
			g.Expect(r.Header.Get("X-Request-Context-ID")).ShouldNot(gomega.BeEmpty())
			var body map[string]any
			err := json.NewDecoder(r.Body).Decode(&body)
			g.Expect(err).ShouldNot(gomega.HaveOccurred())
			g.Expect(body["enabled"]).Should(gomega.Equal(false))
			g.Expect(body["schedule"]).Should(gomega.Equal("BACKUP_SCHEDULE_WEEKLY"))
			g.Expect(body["retentionDays"]).Should(gomega.Equal(float64(30)))
			w.Header().Set("Content-Type", "application/json")
			_, _ = w.Write(
				[]byte(
					`{"enabled":false,"retentionDays":30,"schedule":"BACKUP_SCHEDULE_WEEKLY","serviceInstanceId":"e6291902-f493-443f-b16d-1729ed2c75a9","strongEtag":"cG9saWN5LWV0YWctMg==","version":"2"}`,
				),
			)
			return
		default:
			t.Fatalf("unexpected request: %s %s", r.Method, r.URL.Path)
		}
	}))
	defer server.Close()

	lifecycle := NewSys9Lifecycle(
		&fakeSys9AuthManager{session: &adapter.Sys9Session{AccessToken: "session-token", SessionToken: "session-dpop"}},
		&fakeSys9OEMProfileProvider{profile: &adapter.Sys9OEMProfile{DevToken: "mock-dev-token", DPoPProof: "mock-dpop-proof"}},
	)

	policy, err := lifecycle.GetManagedDatabaseBackupPolicy(t.Context(), &entity.Cluster{Region: &entity.Region{
		ServiceAddress: server.URL,
		Sys9Config:     &globalcommonv1.RegionSys9Config{OemProfileRef: "profile/ref"},
	}}, instanceID)
	g.Expect(err).ShouldNot(gomega.HaveOccurred())
	g.Expect(policy.Schedule).Should(gomega.Equal("BACKUP_SCHEDULE_DAILY"))
	g.Expect(policy.RetentionDays).Should(gomega.Equal(uint32(7)))

	updated, err := lifecycle.UpdateManagedDatabaseBackupPolicy(t.Context(), &entity.Cluster{Region: &entity.Region{
		ServiceAddress: server.URL,
		Sys9Config:     &globalcommonv1.RegionSys9Config{OemProfileRef: "profile/ref"},
	}}, instanceID, &adapter.Sys9ManagedDatabaseBackupPolicy{
		Enabled:       false,
		Schedule:      "BACKUP_SCHEDULE_WEEKLY",
		RetentionDays: 30,
	})
	g.Expect(err).ShouldNot(gomega.HaveOccurred())
	g.Expect(updated.Enabled).Should(gomega.BeFalse())
	g.Expect(updated.Schedule).Should(gomega.Equal("BACKUP_SCHEDULE_WEEKLY"))
	g.Expect(updated.RetentionDays).Should(gomega.Equal(uint32(30)))
}
