package repo_imp

import (
	"context"
	"errors"

	"github.com/tidbcloud/pkgv2/bizerr"
	"github.com/tidbcloud/pkgv2/db"
	"github.com/tidbcloud/pkgv2/tracing"
	"github.com/tidbcloud/serverless-service/internal/domain/constant"
	"github.com/tidbcloud/serverless-service/internal/domain/entity"
	"github.com/tidbcloud/serverless-service/internal/domain/repo"
	"gorm.io/gorm"
)

type auditLogFilterRuleRepoImp struct {
	dbProvider db.Provider
}

func NewAuditLogFilterRuleRepoImp(dbProvider db.Provider) repo.AuditLogFilterRuleRepo {
	return &auditLogFilterRuleRepoImp{dbProvider}
}

func (r *auditLogFilterRuleRepoImp) Create(ctx context.Context, filterRule *entity.AuditLogFilterRule) bizerr.BizError {
	ctx, span := Tracer.Start(ctx, "auditLogFilterRuleRepo.Create")
	defer func() { tracing.EndSpan(span, nil) }()

	wdb := r.dbProvider.WriteDB(ctx)
	if err := wdb.Create(filterRule).Error; err != nil {
		return bizerr.WrapGormError(err, "fail to create audit log filter rule")
	}
	return nil
}

func (r *auditLogFilterRuleRepoImp) GetByID(
	ctx context.Context,
	clusterId, filterRuleId string,
) (*entity.AuditLogFilterRule, bizerr.BizError) {
	ctx, span := Tracer.Start(ctx, "auditLogFilterRuleRepo.GetByID")
	defer func() { tracing.EndSpan(span, nil) }()

	rdb := r.dbProvider.ReadDB(ctx)
	filterRule := &entity.AuditLogFilterRule{}
	if err := rdb.Where("id = ? and cluster_id = ?", filterRuleId, clusterId).First(filterRule).Error; err != nil {
		return nil, bizerr.WrapGormError(err, "fail to get audit log filter rule")
	}
	return filterRule, nil
}

func (r *auditLogFilterRuleRepoImp) ListByFilter(
	ctx context.Context,
	filter entity.AuditLogFilterRuleFilter,
) ([]*entity.AuditLogFilterRule, bizerr.BizError) {
	ctx, span := Tracer.Start(ctx, "auditLogFilterRuleRepo.ListByFilter")
	defer func() { tracing.EndSpan(span, nil) }()

	rdb := r.dbProvider.ReadDB(ctx)
	rdb = buildFilterRuleFilter(filter, rdb)

	var rules []*entity.AuditLogFilterRule
	if err := rdb.Find(&rules).Error; err != nil {
		return nil, bizerr.WrapGormError(err, "fail to list audit log filter rules")
	}
	return rules, nil
}

func (r *auditLogFilterRuleRepoImp) CountByFilter(
	ctx context.Context,
	filter entity.AuditLogFilterRuleFilter,
) (int64, bizerr.BizError) {
	ctx, span := Tracer.Start(ctx, "auditLogFilterRuleRepo.CountByFilter")
	defer func() { tracing.EndSpan(span, nil) }()

	rdb := r.dbProvider.ReadDB(ctx)
	rdb = buildFilterRuleFilterCount(filter, rdb)

	var count int64
	if err := rdb.Model(&entity.AuditLogFilterRule{}).Count(&count).Error; err != nil {
		return 0, bizerr.WrapGormError(err, "fail to count audit log filter rules")
	}
	return count, nil
}

func (r *auditLogFilterRuleRepoImp) Save(ctx context.Context, filterRule *entity.AuditLogFilterRule) bizerr.BizError {
	ctx, span := Tracer.Start(ctx, "auditLogFilterRuleRepo.Save")
	defer func() { tracing.EndSpan(span, nil) }()

	wdb := r.dbProvider.WriteDB(ctx)
	if err := wdb.Save(filterRule).Error; err != nil {
		return bizerr.WrapGormError(err, "fail to save audit log filter rule")
	}
	return nil
}

func (r *auditLogFilterRuleRepoImp) Delete(ctx context.Context, clusterId, filterRuleId string) bizerr.BizError {
	ctx, span := Tracer.Start(ctx, "auditLogFilterRuleRepo.Delete")
	defer func() { tracing.EndSpan(span, nil) }()

	wdb := r.dbProvider.WriteDB(ctx)
	if err := wdb.Where("id = ? and cluster_id = ?", filterRuleId, clusterId).Delete(&entity.AuditLogFilterRule{}).Error; err != nil {
		return bizerr.WrapGormError(err, "fail to delete audit log filter rule")
	}
	return nil
}

func buildFilterRuleFilter(filter entity.AuditLogFilterRuleFilter, db *gorm.DB) *gorm.DB {
	if filter.ClusterID != "" {
		db = db.Where("cluster_id = ?", filter.ClusterID)
	}
	if filter.Offset != nil {
		db = db.Offset(int(*filter.Offset))
	}
	if filter.Limit != nil {
		db = db.Limit(int(*filter.Limit))
	}
	if filter.OrderBy != "" {
		db = db.Order(filter.OrderBy)
	}
	return db
}

func buildFilterRuleFilterCount(filter entity.AuditLogFilterRuleFilter, db *gorm.DB) *gorm.DB {
	if filter.ClusterID != "" {
		db = db.Where("cluster_id = ?", filter.ClusterID)
	}
	return db
}

type auditLogConfigRepoImp struct {
	dbProvider db.Provider
}

func NewAuditLogConfigRepoImp(dbProvider db.Provider) repo.AuditLogConfigRepo {
	return &auditLogConfigRepoImp{dbProvider}
}

func (r *auditLogConfigRepoImp) Get(ctx context.Context, clusterId string) (*entity.AuditLogConfig, bizerr.BizError) {
	ctx, span := Tracer.Start(ctx, "auditLogConfigRepo.Get")
	defer func() { tracing.EndSpan(span, nil) }()

	rdb := r.dbProvider.ReadDB(ctx)
	config := &entity.AuditLogConfig{}
	if err := rdb.Where("cluster_id = ?", clusterId).First(config).Error; err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			enabled := false
			unredacted := false
			return &entity.AuditLogConfig{
				ClusterID:               clusterId,
				Enabled:                 &enabled,
				Unredacted:              &unredacted,
				RotationIntervalSeconds: 0,
				RotationSizeBytes:       0,
				CloudStorageType:        constant.CloudStorageTypeTiDBCloud,
			}, nil
		}
		return nil, bizerr.WrapGormError(err, "fail to get audit log config")
	}
	return config, nil
}

func (r *auditLogConfigRepoImp) Save(ctx context.Context, config *entity.AuditLogConfig) bizerr.BizError {
	ctx, span := Tracer.Start(ctx, "auditLogConfigRepo.Save")
	defer func() { tracing.EndSpan(span, nil) }()

	wdb := r.dbProvider.WriteDB(ctx)
	if err := wdb.Save(config).Error; err != nil {
		return bizerr.WrapGormError(err, "fail to save audit log config")
	}
	return nil
}

func (r *auditLogConfigRepoImp) Create(ctx context.Context, config *entity.AuditLogConfig) bizerr.BizError {
	ctx, span := Tracer.Start(ctx, "auditLogConfigRepo.Create")
	defer func() { tracing.EndSpan(span, nil) }()

	wdb := r.dbProvider.WriteDB(ctx)
	if err := wdb.Create(config).Error; err != nil {
		return bizerr.WrapGormError(err, "fail to create audit log config")
	}
	return nil
}

func (r *auditLogConfigRepoImp) Delete(ctx context.Context, clusterId string) bizerr.BizError {
	ctx, span := Tracer.Start(ctx, "auditLogConfigRepo.Delete")
	defer func() { tracing.EndSpan(span, nil) }()

	wdb := r.dbProvider.WriteDB(ctx)
	if err := wdb.Where("cluster_id = ?", clusterId).Delete(&entity.AuditLogConfig{}).Error; err != nil {
		return bizerr.WrapGormError(err, "fail to delete audit log config")
	}
	return nil
}
