package vercel

import (
	"context"
	serrors "errors"

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

type DataAppConnectionDAO interface {
	AddVercelConnection(ctx context.Context, connection DataAppConnection) (*DataAppConnection, error)
	GetVercelConnection(ctx context.Context, dataAppID uint64, vercelProjectID string) (*DataAppConnection, error)
	ListVercelConnectionsByTiDBProjectID(ctx context.Context, tiDBProjectID uint64) ([]DataAppConnection, error)
	ListVercelConnectionsByOrgID(ctx context.Context, orgID uint64) ([]DataAppConnection, error)
	ListVercelConnectionsByConfigurationID(ctx context.Context, configurationID string) ([]DataAppConnection, error)
	ListVercelConnectionsByProjectIDsAndConfigurationID(ctx context.Context, projectIDs []uint64, configurationID string) ([]DataAppConnection, error)
	GetVercelConnectionByVercelProjectID(ctx context.Context, vercelProjectID string) (*DataAppConnection, error)
	ListAllDataAppIDs(ctx context.Context) ([]uint64, error)
	ListVercelConnectionsByDataAppIDs(ctx context.Context, dataAppIDs []uint64) ([]DataAppConnection, error)
	ListVercelConnections(ctx context.Context) ([]DataAppConnection, error)
}

type dataAppConnectionDAOImpl struct {
	db *gorm.DB
}

func NewDataAppConnectionDAO(db *gorm.DB) DataAppConnectionDAO {
	return &dataAppConnectionDAOImpl{
		db: db,
	}
}

func (d *dataAppConnectionDAOImpl) AddVercelConnection(ctx context.Context, connection DataAppConnection) (_ *DataAppConnection, err error) {
	ctx, span := Tracer.Start(ctx, "dataAppConnectionDAOImpl.AddVercelConnection")
	defer func() {
		tracing.EndSpan(span, err)
	}()

	err = d.db.Clauses(clause.OnConflict{
		Columns:   []clause.Column{{Name: "vercel_project_id"}, {Name: "data_app_id"}},
		DoUpdates: clause.AssignmentColumns([]string{"updated_at"}),
	}).Create(&connection).Error

	if err != nil {
		return &connection, errors.Trace(err)
	}

	tilog.Info(ctx, "Succeed to create connection")
	return &connection, nil
}

func (d *dataAppConnectionDAOImpl) GetVercelConnection(ctx context.Context, dataAppID uint64, vercelProjectID string) (_ *DataAppConnection, err error) {
	_, span := Tracer.Start(ctx, "dataAppConnectionDAOImpl.GetVercelConnection")
	defer func() {
		tracing.EndSpan(span, err)
	}()

	var connection DataAppConnection
	if err = d.db.Model(&DataAppConnection{}).Preload("DataAppEnvironmentIdentifier").Preload("VercelIntegrationConfiguration").Where("data_app_id = ? AND vercel_project_id = ?", dataAppID, vercelProjectID).First(&connection).Error; err != nil {
		if serrors.Is(err, gorm.ErrRecordNotFound) {
			return nil, nil
		} else {
			return nil, errors.Trace(err)
		}
	}

	return &connection, nil
}

func (d *dataAppConnectionDAOImpl) GetVercelConnectionByVercelProjectID(ctx context.Context, vercelProjectID string) (_ *DataAppConnection, err error) {
	_, span := Tracer.Start(ctx, "dataAppConnectionDAOImpl.GetVercelConnectionByVercelProjectID")
	defer func() {
		tracing.EndSpan(span, err)
	}()

	var connection DataAppConnection
	if err = d.db.Model(&DataAppConnection{}).Preload("DataAppEnvironmentIdentifier").Preload("VercelIntegrationConfiguration").Where("vercel_project_id = ?", vercelProjectID).First(&connection).Error; err != nil {
		if serrors.Is(err, gorm.ErrRecordNotFound) {
			return nil, nil
		} else {
			return nil, errors.Trace(err)
		}
	}

	return &connection, nil
}

func (d *dataAppConnectionDAOImpl) ListVercelConnectionsByDataAppIDs(ctx context.Context, dataAppIDs []uint64) (_ []DataAppConnection, err error) {
	_, span := Tracer.Start(ctx, "dataAppConnectionDAOImpl.ListVercelConnectionsByDataAppIDs")
	defer func() {
		tracing.EndSpan(span, err)
	}()

	var records []DataAppConnection
	if err = d.db.Model(&DataAppConnection{}).Preload("DataAppEnvironmentIdentifier").Preload("VercelIntegrationConfiguration").Where("data_app_id in ?", dataAppIDs).Find(&records).Error; err != nil {
		return nil, errors.Trace(err)
	}
	return records, nil
}

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

	var records []DataAppConnection
	if err = d.db.Model(&DataAppConnection{}).Find(&records).Error; err != nil {
		return nil, errors.Trace(err)
	}
	return records, nil
}

func (d *dataAppConnectionDAOImpl) ListVercelConnectionsByTiDBProjectID(ctx context.Context, tiDBProjectID uint64) (_ []DataAppConnection, err error) {
	_, span := Tracer.Start(ctx, "dataAppConnectionDAOImpl.ListVercelConnectionsByTiDBProjectID")
	defer func() {
		tracing.EndSpan(span, err)
	}()

	var records []DataAppConnection
	if err = d.db.Where("tidb_project_id = ?", tiDBProjectID).Find(&records).Error; err != nil {
		return nil, errors.Trace(err)
	}
	return records, nil
}

func (d *dataAppConnectionDAOImpl) ListVercelConnectionsByOrgID(ctx context.Context, orgID uint64) (_ []DataAppConnection, err error) {
	_, span := Tracer.Start(ctx, "dataAppConnectionDAOImpl.ListVercelConnectionsByOrgID")
	defer func() {
		tracing.EndSpan(span, err)
	}()

	var records []DataAppConnection
	if err = d.db.Where("tidb_org_id = ?", orgID).Find(&records).Error; err != nil {
		return nil, errors.Trace(err)
	}
	return records, nil
}

func (d *dataAppConnectionDAOImpl) ListVercelConnectionsByConfigurationID(ctx context.Context, configurationID string) (_ []DataAppConnection, err error) {
	_, span := Tracer.Start(ctx, "dataAppConnectionDAOImpl.ListVercelConnectionsByConfigurationID")
	defer func() {
		tracing.EndSpan(span, err)
	}()

	var records []DataAppConnection
	if err = d.db.Where("vercel_integration_configuration_id = ?", configurationID).Find(&records).Error; err != nil {
		return nil, errors.Trace(err)
	}
	return records, nil
}

func (d *dataAppConnectionDAOImpl) ListVercelConnectionsByProjectIDsAndConfigurationID(ctx context.Context, projectIDs []uint64, configurationID string) (_ []DataAppConnection, err error) {
	_, span := Tracer.Start(ctx, "dataAppConnectionDAOImpl.ListVercelConnectionsByProjectIDsAndConfigurationID")
	defer func() {
		tracing.EndSpan(span, err)
	}()

	var records []DataAppConnection
	if err = d.db.Where("vercel_integration_configuration_id = ? and tidb_project_id IN ?", configurationID, projectIDs).Find(&records).Error; err != nil {
		return nil, errors.Trace(err)
	}
	return records, nil
}

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

	var records []uint64
	if err := d.db.Model(&DataAppConnection{}).Pluck("DataAppID", &records).Error; err != nil {
		return nil, errors.Trace(err)
	}
	return records, nil
}
