package restriction

import (
	"context"
	"kami/internal/consts"
	"kami/internal/dao"
	"kami/internal/model"
	"kami/internal/model/do"
	"kami/internal/model/entity"
	"kami/internal/service"
	"kami/utility/config"
	"kami/utility/integration/restriction"

	"github.com/Lofanmi/pinyin-golang/pinyin"
	"github.com/duke-git/lancet/v2/pointer"
	"github.com/duke-git/lancet/v2/slice"
	"github.com/duke-git/lancet/v2/validator"
	"github.com/gogf/gf/v2/database/gdb"
	"github.com/gogf/gf/v2/text/gstr"
	"github.com/gogf/gf/v2/util/gconv"
)

func init() {
	service.RegisterRestriction(New())
}

func New() *sRestriction {
	return &sRestriction{}
}

type sRestriction struct{}

func (i *sRestriction) GetAllProvince(ctx context.Context) []string {
	return restriction.AllProvinceList
}

func (i *sRestriction) CheckIPIsAllowed(ctx context.Context, ip string, area []string) (isAllowed bool, err error) {
	isAllowed = false
	inArea, _, err := i.IsInProvince(ctx, ip, area)
	if !inArea {
		isAllowed = true
	}
	return isAllowed, err
}

// AddToRestrictIP 把当前IP加入限制IP中
func (i *sRestriction) AddToRestrictIP(ctx context.Context, input model.AddRestrictionRecordInput) (err error) {
	err = config.GetDatabaseV1().Ctx(ctx).Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
		ipRecord, _ := i.GetOrSetIPRecord(ctx, input.Ip, tx)
		_, err = dao.V1RestrictIpOrderAccess.Ctx(ctx).DB(config.GetDatabaseV1()).TX(tx).Insert(do.V1RestrictIpOrderAccess{
			Ip:               input.Ip,
			Status:           input.Status,
			RestrictStrategy: input.Strategy,
			DeviceId:         input.DeviceId,
			RestrictIpId:     ipRecord.Id,
			OrderNo:          input.OrderNo,
			CardPass:         input.CardPass,
		})
		_ = service.OrderSummary().SetBlockStatus(ctx, input.OrderNo, input.Status, tx)
		return nil
	})
	return
}

// SetCurrentOrderDisable 拉黑当前订单
func (i *sRestriction) SetCurrentOrderDisable(ctx context.Context, orderNo string, status consts.RestrictStatus, tx gdb.TX) (err error) {
	m := dao.V1RestrictIpOrderAccess.Ctx(ctx).DB(config.GetDatabaseV1())
	if tx != nil {
		m = m.TX(tx)
	}
	_, err = m.Where(dao.V1RestrictIpOrderAccess.Columns().OrderNo, orderNo).Update(do.V1RestrictIpOrderAccess{
		Status: status,
	})
	_ = service.OrderSummary().SetBlockStatus(ctx, orderNo, status, tx)
	return
}

// GetLocationWithProxyByIP 查看当前IP是否是代理
func (i *sRestriction) GetLocationWithProxyByIP(ctx context.Context, ip string) (data *model.RestrictionIPRecordOutput, err error) {
	err = dao.V1RestrictIpRecord.Ctx(ctx).DB(config.GetDatabaseV1()).
		Where(dao.V1RestrictIpRecord.Columns().Ip, ip).
		Scan(&data)
	return
}

// IsInProvince 判断当前IP是否是下面省份
func (i *sRestriction) IsInProvince(ctx context.Context, ip string, province []string) (isin bool, m *model.RestrictionIPRecordOutput, err error) {
	isin = false
	provinceCopier := append([]string{}, province...)
	ipRecord, err := i.GetOrSetIPRecord(ctx, ip, nil)
	if err != nil || pointer.IsNil(ipRecord) || !ipRecord.IsPublic {
		return isin, ipRecord, err
	}
	//复制一份新的省份列表
	allProvinceListCopier := append([]string{}, restriction.AllProvinceList...)
	//如果返回值是英文
	if validator.IsAlpha(gstr.TrimAll(ipRecord.IpProvince)) {
		ipRecord.IpProvince = gstr.ToLower(ipRecord.IpProvince)
		slice.ForEach(provinceCopier, func(index int, item string) {
			provinceCopier[index] = gstr.ToLower(pinyin.NewDict().Convert(item, "").None())
		})
		slice.ForEachConcurrent(allProvinceListCopier, func(index int, item string) {
			allProvinceListCopier[index] = gstr.ToLower(pinyin.NewDict().Convert(item, "").None())
		}, 5)
	}
	//判断海外的情况
	if gstr.InArray(province, "海外") &&
		!slice.ContainBy(allProvinceListCopier, func(t string) bool { return gstr.Contains(t, ipRecord.IpProvince) }) {
		return true, ipRecord, nil
	}
	isin = slice.ContainBy(provinceCopier, func(t string) bool { return gstr.Contains(t, ipRecord.IpProvince) })
	return isin, ipRecord, nil
}

// GetRestrictRecordByCardPass 查找当前卡密所有的限制记录
func (i *sRestriction) GetRestrictRecordByCardPass(ctx context.Context, cardPass string) (dataList []entity.V1RestrictIpOrderAccess, err error) {
	dataList = []entity.V1RestrictIpOrderAccess{}
	err = dao.V1RestrictIpOrderAccess.Ctx(ctx).DB(config.GetDatabaseV1()).
		Where(dao.V1RestrictIpOrderAccess.Columns().CardPass, cardPass).
		Scan(&dataList)
	return
}

// GetRestrictRecordByIP 查找当前卡密所有的限制记录
func (i *sRestriction) GetRestrictRecordByIP(ctx context.Context, ip string) (dataList []model.RestrictWithIPRecordOutput, err error) {
	err = dao.V1RestrictIpOrderAccess.Ctx(ctx).DB(config.GetDatabaseV1()).WithAll().
		Where(dao.V1RestrictIpRecord.Columns().Ip, ip).Scan(&dataList)
	if err != nil {
		return
	}
	return
}

func (i *sRestriction) GetIPOrderAccessByCardPass(ctx context.Context, cardPass string) (data []entity.V1RestrictIpOrderAccess, err error) {
	err = dao.V1RestrictIpOrderAccess.Ctx(ctx).DB(config.GetDatabaseV1()).
		Where(dao.V1RestrictIpOrderAccess.Columns().CardPass, cardPass).Scan(&data)
	return
}

// IsRestrictExistByCardPass 判断是否存在IP有限制记录
func (i *sRestriction) IsRestrictExistByCardPass(ctx context.Context, cardPass string) (isExist bool) {
	isExist = false
	list, err := i.GetIPOrderAccessByCardPass(ctx, cardPass)
	if err != nil {
		return
	}
	return slice.Some(list, func(index int, item entity.V1RestrictIpOrderAccess) bool {
		return item.Status == gconv.Int(consts.RestrictStatus_Disable)
	})
}

// IsRestrictExistByIP 判断是否存在IP有限制记录
func (i *sRestriction) IsRestrictExistByIP(ctx context.Context, ip string) (isExist bool) {
	isExist = false
	list, err := i.GetRestrictRecordByIP(ctx, ip)
	if err != nil {
		return
	}
	return slice.Some(list, func(index int, item model.RestrictWithIPRecordOutput) bool {
		return item.Status == gconv.Int(consts.RestrictStatus_Disable)
	})
}

// IsRestrictDevice 判断是否禁用设备
func (i *sRestriction) IsRestrictDevice(ctx context.Context, deviceId string) (isRestrict bool) {
	//	如果历史中存在这个设备被禁用的记录，就禁用设备
	var (
		db     = dao.V1RestrictIpOrderAccess.Ctx(ctx).DB(config.GetDatabaseV1())
		column = dao.V1RestrictIpOrderAccess.Columns()
	)
	isRestrict, _ = db.Where(column.DeviceId, deviceId).Where(column.Status, consts.RestrictStatus_Disable).Exist()
	return
}
