package export

import (
	"context"
	"errors"

	"github.com/tidbcloud/cluster-service-ng/domain/entity"
	"github.com/tidbcloud/cluster-service-ng/pkg/storage/mysql/schema"
	"github.com/tidbcloud/cluster-service-ng/pkg/storage/repo"
	"github.com/tidbcloud/pkgv2/db"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
	"gorm.io/gorm"
)

type exportRepoImpl struct {
	provider db.Provider
}

func NewExportRepo(provider db.Provider) repo.ExportRepo {
	return &exportRepoImpl{
		provider: provider,
	}
}

func (r *exportRepoImpl) Create(ctx context.Context, export *entity.Export) error {
	model, err := r.cvtExportToModel(export)
	if err != nil {
		return status.Errorf(codes.InvalidArgument, "failed to convert export to schema: %v", err)
	}
	if err := r.provider.WriteDB(ctx).Create(model).Error; err != nil {
		if errors.Is(err, gorm.ErrDuplicatedKey) {
			return status.Errorf(codes.AlreadyExists, "export %s already exists", export.GetExportId())
		}
		return status.Errorf(codes.Internal, "failed to create export: %v", err)
	}
	return nil
}

func (r *exportRepoImpl) Save(ctx context.Context, export *entity.Export) error {
	model, err := r.cvtExportToModel(export)
	if err != nil {
		return status.Errorf(codes.InvalidArgument, "failed to convert export to schema: %v", err)
	}
	if err := r.provider.WriteDB(ctx).Save(model).Error; err != nil {
		return status.Errorf(codes.Internal, "failed to save export: %v", err)
	}
	return nil
}

func (r *exportRepoImpl) Load(ctx context.Context, exportID string, includeDeleted bool) (*entity.Export, error) {
	if exportID == "" {
		return nil, status.Errorf(codes.InvalidArgument, "export id cannot be empty")
	}
	rdb := r.provider.ReadDB(ctx)
	if includeDeleted {
		rdb = rdb.Unscoped()
	}

	var model schema.Export
	if err := rdb.Where("export_id = ?", exportID).First(&model).Error; err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, status.Errorf(codes.NotFound, "export %s not found", exportID)
		}
		return nil, status.Errorf(codes.Internal, "failed to load export: %v", err)
	}
	return r.cvtModelToExport(&model)
}

func (r *exportRepoImpl) List(ctx context.Context, filter *repo.ExportListFilter) ([]*entity.Export, error) {
	rdb := r.provider.ReadDB(ctx).Model(&schema.Export{})
	if filter == nil {
		filter = &repo.ExportListFilter{}
	}
	if filter.ClusterID != "" {
		rdb = rdb.Where("cluster_id = ?", filter.ClusterID)
	}
	if len(filter.States) > 0 {
		rdb = rdb.Where("state IN ?", filter.States)
	}

	var models []schema.Export
	if err := rdb.Find(&models).Error; err != nil {
		return nil, status.Errorf(codes.Internal, "failed to list exports: %v", err)
	}
	result := make([]*entity.Export, 0, len(models))
	for i := range models {
		pb, cvtErr := r.cvtModelToExport(&models[i])
		if cvtErr != nil {
			return nil, status.Errorf(codes.Internal, "failed to convert export schema: %v", cvtErr)
		}
		result = append(result, pb)
	}
	return result, nil
}
