package git

import (
	"context"
	goerror "errors"
	"fmt"

	"github.com/pingcap/errors"
	"github.com/tidbcloud/pkgv2/tracing"
	"github.com/tidbcloud/tiinf/monitor/tilog"
	"gorm.io/gorm"
	"gorm.io/gorm/clause"
)

type TiDBGitIntegrationDAO interface {
	GetGitIntegrationByClusterID(ctx context.Context, clusterId uint64) (*TiDBGitConnection, error)
	GetGitIntegrationByRepo(ctx context.Context, repo string, provider GitProvider) (*TiDBGitConnection, error)
	AddGitIntegration(ctx context.Context, integration TiDBGitConnection) (*TiDBGitConnection, error)
	UpsertGitIntegration(ctx context.Context, integration TiDBGitConnection) (*TiDBGitConnection, error)
	DeleteGitIntegrationByClusterID(ctx context.Context, clusterId uint64) (int64, error)
	ListGitIntegrationByProjectID(ctx context.Context, projectId uint64) ([]*TiDBGitConnection, error)
	ListAllGitIntegrations(ctx context.Context) ([]*TiDBGitConnection, error)
	DeleteGitIntegrationByClusterIDs(ctx context.Context, clusterIds []uint64) (int64, error)
	CountGitIntegrations(ctx context.Context) (int64, error)
}

type tiDBGitIntegrationDAOImpl struct {
	db *gorm.DB
}

func NewTiDBGitIntegrationDAO(db *gorm.DB) TiDBGitIntegrationDAO {
	return &tiDBGitIntegrationDAOImpl{
		db: db,
	}
}

func (d *tiDBGitIntegrationDAOImpl) GetGitIntegrationByClusterID(ctx context.Context, clusterId uint64) (_ *TiDBGitConnection, err error) {
	ctx, span := Tracer.Start(ctx, "tiDBGitIntegrationDAOImpl.GetGitIntegrationByClusterID")
	defer func() {
		tracing.EndSpan(span, err)
	}()

	tilog.Debug(ctx, fmt.Sprintf("GetGitIntegrationByClusterID,clusterId:%d", clusterId))
	var integration TiDBGitConnection
	if result := d.db.Where("tidb_cluster_id = ?", clusterId).First(&integration); result.Error != nil {
		if goerror.Is(result.Error, gorm.ErrRecordNotFound) {
			return nil, nil
		} else {
			return nil, errors.Trace(result.Error)
		}
	}
	return &integration, nil
}

func (d *tiDBGitIntegrationDAOImpl) GetGitIntegrationByRepo(ctx context.Context, repo string, provider GitProvider) (_ *TiDBGitConnection, err error) {
	ctx, span := Tracer.Start(ctx, "tiDBGitIntegrationDAOImpl.GetGitIntegrationByRepo")
	defer func() {
		tracing.EndSpan(span, err)
	}()

	tilog.Debug(ctx, fmt.Sprintf("GetGitIntegrationByRepo,repo:%s,provider:%s", repo, string(provider)))
	var integration TiDBGitConnection
	if result := d.db.Where("repo_name = ? and git_provider = ?", repo, string(provider)).First(&integration); result.Error != nil {
		if goerror.Is(result.Error, gorm.ErrRecordNotFound) {
			return nil, nil
		} else {
			return nil, errors.Trace(result.Error)
		}
	}
	return &integration, nil
}

func (d *tiDBGitIntegrationDAOImpl) AddGitIntegration(ctx context.Context, integration TiDBGitConnection) (_ *TiDBGitConnection, err error) {
	ctx, span := Tracer.Start(ctx, "tiDBGitIntegrationDAOImpl.AddGitIntegration")
	defer func() {
		tracing.EndSpan(span, err)
	}()

	tilog.Debug(ctx, fmt.Sprintf("AddGitIntegration,integration:%+v", integration))
	if result := d.db.Create(&integration); result.Error != nil {
		return &integration, errors.Trace(result.Error)
	}
	return &integration, nil
}

func (d *tiDBGitIntegrationDAOImpl) UpsertGitIntegration(ctx context.Context, integration TiDBGitConnection) (_ *TiDBGitConnection, err error) {
	ctx, span := Tracer.Start(ctx, "tiDBGitIntegrationDAOImpl.UpsertGitIntegration")
	defer func() {
		tracing.EndSpan(span, err)
	}()

	tilog.Debug(ctx, fmt.Sprintf("UpsertGitIntegration,integration:%+v", integration))
	if result := d.db.Clauses(clause.OnConflict{
		Columns:   []clause.Column{{Name: "tidb_cluster_id"}},
		DoUpdates: clause.AssignmentColumns([]string{"git_provider", "repo_name"}),
	}).Create(&integration); result.Error != nil {
		return &integration, errors.Trace(result.Error)
	}
	return &integration, nil
}

func (d *tiDBGitIntegrationDAOImpl) DeleteGitIntegrationByClusterID(ctx context.Context, clusterId uint64) (_ int64, err error) {
	ctx, span := Tracer.Start(ctx, "tiDBGitIntegrationDAOImpl.DeleteGitIntegrationByClusterID")
	defer func() {
		tracing.EndSpan(span, err)
	}()

	tilog.Debug(ctx, fmt.Sprintf("DeleteGitIntegrationByClusterID,clusterId:%d", clusterId))
	result := d.db.Where("tidb_cluster_id = ?", clusterId).Delete(&TiDBGitConnection{})
	if result.Error != nil {
		return 0, errors.Trace(result.Error)
	}
	return result.RowsAffected, nil
}

func (d *tiDBGitIntegrationDAOImpl) ListGitIntegrationByProjectID(ctx context.Context, projectId uint64) (_ []*TiDBGitConnection, err error) {
	ctx, span := Tracer.Start(ctx, "tiDBGitIntegrationDAOImpl.ListGitIntegrationByProjectID")
	defer func() {
		tracing.EndSpan(span, err)
	}()

	tilog.Debug(ctx, fmt.Sprintf("ListGitIntegrationByClusterID,projectId:%d", projectId))
	var integration []*TiDBGitConnection
	if result := d.db.Where("tidb_project_id = ?", projectId).Find(&integration); result.Error != nil {
		return nil, errors.Trace(result.Error)
	}
	return integration, nil
}

func (d *tiDBGitIntegrationDAOImpl) ListAllGitIntegrations(ctx context.Context) (_ []*TiDBGitConnection, err error) {
	ctx, span := Tracer.Start(ctx, "tiDBGitIntegrationDAOImpl.ListAllGitIntegrations")
	defer func() {
		tracing.EndSpan(span, err)
	}()

	tilog.Debug(ctx, "ListAllGitIntegrations")
	var integration []*TiDBGitConnection
	if result := d.db.Find(&integration); result.Error != nil {
		return nil, errors.Trace(result.Error)
	}
	return integration, nil
}

func (d *tiDBGitIntegrationDAOImpl) DeleteGitIntegrationByClusterIDs(ctx context.Context, clusterIds []uint64) (_ int64, err error) {
	ctx, span := Tracer.Start(ctx, "tiDBGitIntegrationDAOImpl.DeleteGitIntegrationByClusterIDs")
	defer func() {
		tracing.EndSpan(span, err)
	}()

	tilog.Debug(ctx, fmt.Sprintf("DeleteGitIntegrationByClusterIDs,clusterIds:%v", clusterIds))
	result := d.db.Where("tidb_cluster_id in ?", clusterIds).Delete(&TiDBGitConnection{})
	if result.Error != nil {
		return 0, errors.Trace(result.Error)
	}
	return result.RowsAffected, nil
}

func (d *tiDBGitIntegrationDAOImpl) CountGitIntegrations(ctx context.Context) (_ int64, err error) {
	ctx, span := Tracer.Start(ctx, "tiDBGitIntegrationDAOImpl.CountGitIntegrations")
	defer func() {
		tracing.EndSpan(span, err)
	}()
	var count int64
	tilog.Debug(ctx, "CountGitIntegrations")
	if result := d.db.Model(&TiDBGitConnection{}).Count(&count); result.Error != nil {
		return 0, errors.Trace(result.Error)
	}
	return count, nil
}
