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 GlobalDiscount struct {
	Id                  uint64
	OrgId               uint64
	Discount            float64
	DiscountDescription string
	StartTime           int64
	EndTime             int64
}

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

	err := db.Transaction(func(tx *gorm.DB) error {
		err := dbDiscount.Create(tx)
		if err != nil {
			return err
		}
		desc := fmt.Sprintf("add global discount %f", discount.Discount)
		return dbutil.AddAccessLogNow(tx, m.tenantAndUser.User.TenantID, m.tenantAndUser.Email, desc)
	})
	if err != nil {
		return nil, err
	}
	return buildInternalGlobalDiscount(dbDiscount), nil
}

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

	var dbDiscount entity.GlobalDiscount
	err := db.Transaction(func(tx *gorm.DB) error {
		if err := entity.NewGlobalDiscountQuerySet(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.GlobalDiscountDBSchema.Discount,
			entity.GlobalDiscountDBSchema.Description,
			entity.GlobalDiscountDBSchema.StartTime,
			entity.GlobalDiscountDBSchema.EndTime,
		); err != nil {
			return err
		}
		return dbutil.AddAccessLogNow(tx, m.tenantAndUser.User.TenantID, m.tenantAndUser.Email,
			fmt.Sprintf("update global discount %d %f for tenant %d", discount.Id, discount.Discount, discount.OrgId))
	})
	if err != nil {
		return nil, err
	}
	return buildInternalGlobalDiscount(dbDiscount), nil
}

func (m UserContext) DeleteGlobalDiscount(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.GlobalDiscount{
			Base: entity.Base{
				ID: entity.ID(discountID),
			},
		}
		err := discount.Delete(tx)
		if err != nil {
			return err
		}
		desc := fmt.Sprintf("delete global discount %d", discountID)
		return dbutil.AddAccessLogNow(tx, m.tenantAndUser.User.TenantID, m.tenantAndUser.Email, desc)
	})
}

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

	var dbDiscounts []entity.GlobalDiscount
	err := db.Transaction(func(tx *gorm.DB) error {
		return entity.NewGlobalDiscountQuerySet(tx).All(&dbDiscounts)
	})
	if err != nil {
		return nil, err
	}

	var discounts []*GlobalDiscount
	for _, item := range dbDiscounts {
		discounts = append(discounts, buildInternalGlobalDiscount(item))
	}
	return discounts, nil
}

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

func validateGlobalDiscount(discount *GlobalDiscount) 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 buildGlobalDiscount(discount *GlobalDiscount) entity.GlobalDiscount {
	dbDiscount := entity.GlobalDiscount{
		Discount:    discount.Discount,
		Description: discount.DiscountDescription,
		StartTime:   time.Unix(discount.StartTime, 0),
		EndTime:     time.Unix(discount.EndTime, 0),
	}
	dbDiscount.ID = entity.ID(discount.Id)
	return dbDiscount
}
