package admin

import (
	"fmt"
	"time"

	"github.com/pingcap/errcode"
	"github.com/pingcap/errors"
	"github.com/tidbcloud/central-sdk/db"
	"github.com/tidbcloud/central-sdk/db/dbutil"
	"github.com/tidbcloud/central-sdk/db/entity"
	"gorm.io/gorm"
)

type Discount struct {
	Id                  uint64
	OrgId               uint64
	Discount            float64
	DiscountDescription string
	StartTime           int64
	EndTime             int64
}

func (m UserContext) AddDiscount(discount *Discount) (*Discount, error) {
	if err := validateDiscount(discount); err != nil {
		return nil, err
	}
	if m.tenantAndUser.ID == 0 {
		return nil, errcode.NewInvalidInputErr(errors.New("not a normal user"))
	}
	dbDiscount := buildDiscount(discount)

	err := db.Transaction(func(tx *gorm.DB) error {
		return dbDiscount.Create(tx)
	})
	if err != nil {
		return nil, err
	}

	_ = db.Transaction(func(tx *gorm.DB) error {
		return dbutil.AddAccessLogNow(tx, m.tenantAndUser.User.TenantID, m.tenantAndUser.Email,
			fmt.Sprintf("add new discount %f for tenant %d", discount.Discount, discount.OrgId))
	})
	return buildInternalDiscount(dbDiscount), nil
}

func (m UserContext) UpdateDiscount(discount *Discount) (*Discount, error) {
	if err := validateDiscount(discount); err != nil {
		return nil, err
	}
	if m.tenantAndUser.ID == 0 {
		return nil, errcode.NewInvalidInputErr(errors.New("not a normal user"))
	}

	var dbDiscount entity.Discount
	err := db.Transaction(func(tx *gorm.DB) error {
		if err := entity.NewDiscountQuerySet(tx).IDEq(entity.ID(discount.Id)).One(&dbDiscount); err != nil {
			return nil
		}
		dbDiscount.Description = discount.DiscountDescription
		dbDiscount.Discount = discount.Discount
		dbDiscount.StartTime = time.Unix(discount.StartTime, 0)
		dbDiscount.EndTime = time.Unix(discount.EndTime, 0)
		if err := dbDiscount.Update(tx,
			entity.DiscountDBSchema.Discount,
			entity.DiscountDBSchema.Description,
			entity.DiscountDBSchema.StartTime,
			entity.DiscountDBSchema.EndTime); err != nil {
			return err
		}
		return dbutil.AddAccessLogNow(tx, m.tenantAndUser.User.TenantID, m.tenantAndUser.Email,
			fmt.Sprintf("update discount %d %f for tenant %d", discount.Id, discount.Discount, discount.OrgId))
	})
	if err != nil {
		return nil, err
	}
	return buildInternalDiscount(dbDiscount), nil
}

func (m UserContext) DeleteDiscount(tenantID, discountID uint64) error {
	if m.tenantAndUser.ID == 0 {
		return errors.New("not a normal user")
	}

	return db.Transaction(func(tx *gorm.DB) error {
		discount := entity.Discount{
			Base: entity.Base{
				ID: entity.ID(discountID),
			},
			TenantID: entity.ID(tenantID),
		}
		err := discount.Delete(tx)
		if err != nil {
			return err
		}
		return dbutil.AddAccessLogNow(tx, m.tenantAndUser.User.TenantID, m.tenantAndUser.Email, fmt.Sprintf("delete discount %d for tenant %d", discountID, tenantID))
	})
}

func (m UserContext) ListDiscount(tenantID uint64) ([]*Discount, error) {
	if m.tenantAndUser.ID == 0 {
		return nil, errors.New("not a normal user")
	}

	var dbDiscounts []entity.Discount
	err := db.Transaction(func(tx *gorm.DB) error {
		return entity.NewDiscountQuerySet(tx).TenantIDEq(entity.ID(tenantID)).All(&dbDiscounts)
	})
	if err != nil {
		return nil, err
	}
	var discounts []*Discount
	for _, item := range dbDiscounts {
		discounts = append(discounts, buildInternalDiscount(item))
	}
	return discounts, nil
}

func buildInternalDiscount(item entity.Discount) *Discount {
	return &Discount{
		Id:                  uint64(item.ID),
		Discount:            item.Discount,
		DiscountDescription: item.Description,
		StartTime:           item.StartTime.Unix(),
		EndTime:             item.EndTime.Unix(),
	}
}

func validateDiscount(discount *Discount) error {
	if discount.DiscountDescription == "" {
		return errcode.NewInvalidInputErr(errors.Errorf("invalid discount description %s", discount.DiscountDescription))
	}
	if discount.Discount >= 1 || discount.Discount <= 0 {
		return errcode.NewInvalidInputErr(errors.Errorf("invalid discount %f", discount.Discount))
	}
	if discount.EndTime < time.Now().Unix() {
		return errcode.NewInvalidInputErr(errors.Errorf("invalid discount end time %d", discount.EndTime))
	}
	return nil
}

func buildDiscount(discount *Discount) entity.Discount {
	dbDiscount := entity.Discount{
		TenantID:     entity.ID(discount.OrgId),
		Discount:     discount.Discount,
		Description:  discount.DiscountDescription,
		DiscountType: entity.Normal,
		StartTime:    time.Unix(discount.StartTime, 0),
		EndTime:      time.Unix(discount.EndTime, 0),
	}
	dbDiscount.ID = entity.ID(discount.Id)
	return dbDiscount
}
