package billing

import (
	"fmt"

	"github.com/pingcap/dbaas/billing2/billing_errors"
	"github.com/pingcap/dbaas/billing2/db"
	"github.com/pingcap/dbaas/billing2/db/entity"
	"github.com/pingcap/dbaas/billing2/forms"
	"github.com/pingcap/dbaas/billing2/utils/errorutils"
	"gorm.io/gorm/clause"
)

type UnitModel struct {
}

func unitFromEntity(unit *entity.Unit) *forms.UnitInfo {
	return &forms.UnitInfo{
		UnitKey:     unit.UnitKey,
		Description: unit.Description,
		Category:    unit.Category,
	}
}

func (model *UnitModel) GetAllUnits() ([]forms.UnitInfo, error) {
	var units []entity.Unit
	err := entity.NewUnitQuerySet(db.GetDB()).All(&units)
	if err != nil {
		return nil, billing_errors.NewSystemError(err)
	}
	var result []forms.UnitInfo = make([]forms.UnitInfo, len(units))
	for i, unit := range units {
		result[i] = *unitFromEntity(&unit)
	}
	return result, nil
}

func (model *UnitModel) GetUnit(unitKey string) (*forms.UnitInfo, error) {
	unit := &entity.Unit{}
	err := errorutils.WrapSQLError(entity.NewUnitQuerySet(db.GetDB()).UnitKeyEq(unitKey).One(unit), fmt.Sprintf("unit %v", unitKey))
	if err != nil {
		return nil, err
	}
	return unitFromEntity(unit), nil
}

func (model *UnitModel) CreateUnit(unitInfo *forms.UnitInfo) (*forms.UnitInfo, error) {
	return model.CreateAndUpdateUnit(unitInfo)
}

func (model *UnitModel) CreateAndUpdateUnit(unitInfo *forms.UnitInfo) (*forms.UnitInfo, error) {
	unit := &entity.Unit{
		UnitKey:     unitInfo.UnitKey,
		Description: unitInfo.Description,
		Category:    unitInfo.Category,
	}
	err := db.GetDB().Clauses(clause.OnConflict{
		UpdateAll: true,
	}).Create(unit).Error
	if err != nil {
		return nil, billing_errors.NewSystemError(err)
	}
	return unitFromEntity(unit), nil
}

func (model *UnitModel) UpdateUnit(unitInfo *forms.UnitInfo) (*forms.UnitInfo, error) {
	unit := &entity.Unit{
		UnitKey:     unitInfo.UnitKey,
		Description: unitInfo.Description,
		Category:    unitInfo.Category,
	}
	_, err := model.GetUnit(unit.UnitKey)
	if err != nil {
		return nil, err
	}
	err = unit.Update(db.GetDB(), entity.UnitDBSchema.Description, entity.UnitDBSchema.Category)
	if err != nil {
		return nil, billing_errors.NewSystemError(err)
	}
	return unitFromEntity(unit), nil
}

func (model *UnitModel) DeleteUnit(unitKey string) error {
	err := errorutils.WrapSQLError(entity.NewUnitQuerySet(db.GetDB()).UnitKeyEq(unitKey).Delete(), fmt.Sprintf("unit %v", unitKey))
	if err != nil {
		return err
	}
	return nil
}
