package repo_imp

import (
	"context"

	"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"
)

var _ repo.CanaryRepo = &canaryRepo{}

type canaryRepo struct {
	dbProvider db.Provider
}

func NewCanaryRepo(dbProvider db.Provider) repo.CanaryRepo {
	return &canaryRepo{
		dbProvider: dbProvider,
	}
}

func (c *canaryRepo) AddClusters(ctx context.Context, canaries []*repo.CanaryCluster) (err bizerr.BizError) {
	ctx, span := Tracer.Start(ctx, "canaryRepo.AddClusters")
	defer func() { tracing.EndSpan(span, err) }()

	events := make([]*entity.ClusterTidbPool, len(canaries))
	for i := range events {
		events[i] = &entity.ClusterTidbPool{
			Type:       constant.TypeCanary,
			CanaryMark: true,

			ClusterID: canaries[i].ClusterID,
			ProjectID: canaries[i].ProjectID,

			Status: constant.StatusUnsynced,
		}
	}
	wdb := c.dbProvider.WriteDB(ctx)
	if err := wdb.Create(events).Error; err != nil {
		return bizerr.WrapGormError(err, "failed to add cluster canaries: %v", canaries)
	}
	return nil
}

func (c *canaryRepo) RemoveClusters(ctx context.Context, canaries []*repo.CanaryCluster) (err bizerr.BizError) {
	ctx, span := Tracer.Start(ctx, "canaryRepo.RemoveClusters")
	defer tracing.EndSpan(span, err)

	events := make([]*entity.ClusterTidbPool, len(canaries))
	for i := range events {
		events[i] = &entity.ClusterTidbPool{
			Type:       constant.TypeCanary,
			CanaryMark: false,

			ClusterID: canaries[i].ClusterID,
			ProjectID: canaries[i].ProjectID,

			Status: constant.StatusUnsynced,
		}
	}
	wdb := c.dbProvider.WriteDB(ctx)
	if err := wdb.Create(events).Error; err != nil {
		return bizerr.WrapGormError(err, "failed to remove cluster canaries: %v", canaries)
	}
	return nil
}

func (c *canaryRepo) ListClusters(ctx context.Context) (canaries []*repo.CanaryCluster, err bizerr.BizError) {
	ctx, span := Tracer.Start(ctx, "canaryRepo.ListClusters")
	defer tracing.EndSpan(span, err)

	rdb := c.dbProvider.ReadDB(ctx)
	stmt := rdb.Model(&entity.Cluster{}).Where("canary_mark=?", true)
	if err := stmt.Find(&canaries).Error; err != nil {
		return nil, bizerr.WrapGormError(err, "failed to list cluster canaries")
	}

	return canaries, nil
}

func (c *canaryRepo) GetProject(
	ctx context.Context,
	projectID uint64,
) (project *entity.ProjectCanary, err bizerr.BizError) {
	ctx, span := Tracer.Start(ctx, "canaryRepo.GetProject")
	defer tracing.EndSpan(span, err)

	project = &entity.ProjectCanary{}
	rdb := c.dbProvider.ReadDB(ctx)
	if err := rdb.Model(&entity.ProjectCanary{}).
		Where("project_id=?", projectID).First(project).Error; err != nil {
		return nil, bizerr.WrapGormError(err, "failed to get project canary: %d", projectID)
	}
	return project, nil
}

func (c *canaryRepo) AddProject(ctx context.Context, projectID uint64) (err bizerr.BizError) {
	ctx, span := Tracer.Start(ctx, "canaryRepo.AddProject")
	defer tracing.EndSpan(span, err)

	wdb := c.dbProvider.WriteDB(ctx)
	if err := wdb.Save(&entity.ProjectCanary{ProjectID: projectID}).Error; err != nil {
		return bizerr.WrapGormError(err, "failed to create project canary record: %d", projectID)
	}

	return nil
}

func (c *canaryRepo) RemoveProject(ctx context.Context, projectID uint64) (err bizerr.BizError) {
	ctx, span := Tracer.Start(ctx, "canaryRepo.RemoveProject")
	defer tracing.EndSpan(span, err)

	wdb := c.dbProvider.WriteDB(ctx)
	if err := wdb.Delete(&entity.ProjectCanary{ProjectID: projectID}).Error; err != nil {
		return bizerr.WrapGormError(err, "failed to delete project canary record: %d", projectID)
	}

	return nil
}

func (c *canaryRepo) ListProjects(ctx context.Context) (projects []*entity.ProjectCanary, err bizerr.BizError) {
	ctx, span := Tracer.Start(ctx, "canaryRepo.ListProjects")
	defer tracing.EndSpan(span, err)

	rdb := c.dbProvider.ReadDB(ctx)
	if err := rdb.Model(&entity.ProjectCanary{}).Find(&projects).Error; err != nil {
		return nil, bizerr.WrapGormError(err, "failed to list project canaries")
	}

	return projects, nil
}

func (c *canaryRepo) ListClustersInProject(
	ctx context.Context,
	projectID uint64,
) (clusters []*repo.CanaryCluster, err bizerr.BizError) {
	ctx, span := Tracer.Start(ctx, "canaryRepo.ListClustersInProject")
	defer tracing.EndSpan(span, err)

	rdb := c.dbProvider.ReadDB(ctx)
	if err := rdb.Model(&entity.Cluster{}).Where("project_id=?", projectID).Find(&clusters).Error; err != nil {
		return nil, bizerr.WrapGormError(err, "failed to list cluster in project: %d", projectID)
	}

	return clusters, nil
}
