/*
Copyright 2024.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package legacy

import (
	"context"
	"fmt"

	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"

	o11yv1 "github.com/tidbcloud/observability/api/regionalserver/resourceworker/v1"
	"github.com/tidbcloud/observability/pkg/common/constants"
	"github.com/tidbcloud/observability/pkg/common/deploy"
	task "github.com/tidbcloud/observability/pkg/resourceworker/controllers/integrationtask/v2/task"
)

const (
	// s3BucketNamespace is where S3 custom resources live; the s3_controller
	// reconciles them into real buckets.
	s3BucketNamespace = "observability"
	// defaultExternalRetentionDays bounds how long exported data is kept in a
	// tenant export bucket (V2 default: 14 days).
	defaultExternalRetentionDays = int32(14)
)

// S3Bucket provisions a dedicated per-(tenant, region) destination bucket for
// exclusive external export tasks by creating an S3 custom resource; the
// s3_controller then creates the bucket and applies the retention lifecycle.
// Each tenant's exported data lands in its own bucket so it can be billed
// independently.
type S3Bucket struct {
	*task.BaseTask
}

func NewS3Bucket(ctx *task.Context) *S3Bucket {
	return &S3Bucket{BaseTask: &task.BaseTask{Context: ctx}}
}

func (t *S3Bucket) Name() string {
	return "S3Bucket"
}

func (t *S3Bucket) Create(ctx context.Context) deploy.Result {
	tenantID := t.Task.Labels[constants.LabelTenantID]
	sharedBucketTenant := t.ServiceInfo.ControllerConfig.IntegrationTaskConfig.IsSharedBucketTenant(tenantID)
	policy := o11yv1.ResolveBucketSharePolicy(t.Task.Spec.BucketSharePolicy, sharedBucketTenant)
	if policy != o11yv1.BucketSharePolicyExclusive {
		return deploy.Done()
	}

	switch t.ServiceInfo.Vendor {
	case string(constants.AWS), string(constants.AliCloud):
	default:
		return deploy.Done()
	}

	// Only provision the dedicated per-tenant bucket. A task without a tenant id
	// falls back to the shared external bucket, which must never receive the
	// tenant retention policy.
	if tenantID == "" {
		return deploy.Done()
	}
	bucketName := t.Task.Spec.DestinationBucketName
	if bucketName == "" {
		return deploy.Done()
	}

	s3Obj := &o11yv1.S3{
		ObjectMeta: metav1.ObjectMeta{
			Name:      bucketName,
			Namespace: s3BucketNamespace,
		},
	}
	_, err := controllerutil.CreateOrUpdate(ctx, t.Client, s3Obj, func() error {
		s3Obj.Spec.Name = bucketName
		s3Obj.Spec.ExpirationDays = defaultExternalRetentionDays
		return nil
	})
	if err != nil {
		return deploy.Error(fmt.Errorf("failed to create/update S3 CR for bucket %s: %w", bucketName, err))
	}

	t.Logger.Info("ensured per-tenant export bucket",
		"bucket", bucketName,
		"bucketSharePolicy", policy,
		"expirationDays", defaultExternalRetentionDays)
	return deploy.Done()
}

// Delete is intentionally a no-op: export buckets are retained on pipeline
// deletion because the customer may still be reading exported data and other
// cluster pipelines under the same tenant share this bucket.
func (t *S3Bucket) Delete(ctx context.Context) deploy.Result {
	return deploy.Done()
}
