package service

import (
	"context"
	"errors"
	"fmt"
	v1 "github.com/go-nunu/nunu-layout-advanced/api/v1"
	"github.com/go-nunu/nunu-layout-advanced/internal/model"
	"github.com/go-nunu/nunu-layout-advanced/internal/repository"
	"github.com/mozillazg/go-pinyin"
	"github.com/spf13/viper"
	"golang.org/x/sync/errgroup"
	"gorm.io/gorm"
	"strconv"
	"strings"
	"time"
)

type GlobalLimitService interface {
	GetGlobalLimit(ctx context.Context, id int64) (*model.GlobalLimit, error)
	AddGlobalLimit(ctx context.Context, req v1.GlobalLimitRequest) error
	EditGlobalLimit(ctx context.Context, req v1.GlobalLimitRequest) error
	DeleteGlobalLimit(ctx context.Context, req v1.GlobalLimitRequest) error
	EditGlobalLimitBySnail(ctx context.Context, req v1.GlobalLimitEditRequest) error
}

func NewGlobalLimitService(
	service *Service,
	globalLimitRepository repository.GlobalLimitRepository,
	duedate DuedateService,
	crawler CrawlerService,
	conf *viper.Viper,
	required RequiredService,
	parser ParserService,
	host HostService,
	tcpLimit TcpLimitService,
	udpLimit UdpLimitService,
	webLimit WebLimitService,
	gateWayGroup GatewayGroupService,
	hostRep repository.HostRepository,
	gateWayGroupRep repository.GatewayGroupRepository,
	cdnService CdnService,
	cdnRep repository.CdnRepository,
) GlobalLimitService {
	return &globalLimitService{
		Service:               service,
		globalLimitRepository: globalLimitRepository,
		duedate:               duedate,
		crawler:               crawler,
		Url:                   conf.GetString("crawler.Url"),
		required:              required,
		parser:                parser,
		host:                  host,
		tcpLimit:              tcpLimit,
		udpLimit:              udpLimit,
		webLimit:              webLimit,
		gateWayGroup:          gateWayGroup,
		hostRep:                hostRep,
		gateWayGroupRep:       gateWayGroupRep,
		cdnService:            cdnService,
		cdnRep:                cdnRep,
	}
}

type globalLimitService struct {
	*Service
	globalLimitRepository repository.GlobalLimitRepository
	duedate               DuedateService
	crawler               CrawlerService
	Url                   string
	required              RequiredService
	parser                ParserService
	host                  HostService
	tcpLimit              TcpLimitService
	udpLimit              UdpLimitService
	webLimit              WebLimitService
	gateWayGroup          GatewayGroupService
	hostRep               repository.HostRepository
	gateWayGroupRep       repository.GatewayGroupRepository
	cdnService            CdnService
	cdnRep                repository.CdnRepository
}

func (s *globalLimitService) GetCdnUserId(ctx context.Context, uid int64) (int64, error) {
	data, err := s.globalLimitRepository.GetGlobalLimitFirst(ctx, uid)
	if err != nil {
		if !errors.Is(err, gorm.ErrRecordNotFound) {
			return 0, err
		}
	}
	if data != nil && data.CdnUid != 0 {
		return int64(data.CdnUid), nil
	}
	userInfo,err := s.globalLimitRepository.GetUserInfo(ctx, uid)
	if err != nil {
		return 0, err
	}
	// 中文转拼音
	a := pinyin.NewArgs()
	a.Style = pinyin.Normal
	pinyinSlice := pinyin.LazyPinyin(userInfo.Username, a)
	userName := strconv.Itoa(int(uid)) + "_" + strings.Join(pinyinSlice, "_")
	// 查询用户是否存在
	UserId,err := s.cdnRep.GetUserId(ctx, userName)
	if err != nil {
		return 0, err
	}
	if UserId != 0 {
		return UserId, nil
	}
	// 注册用户
	userId, err := s.cdnService.AddUser(ctx, v1.User{
		Username: userName,
		Email:    userInfo.Email,
		Fullname: userInfo.Username,
		Mobile:   userInfo.PhoneNumber,
	})
	if err != nil {
		return 0, err
	}
	return userId, nil
}

func (s *globalLimitService) AddGroupId(ctx context.Context,groupName string) (int64, error)  {
	groupId, err := s.cdnService.CreateGroup(ctx, v1.Group{
		Name: groupName,
	})
	if err != nil {
		return 0, err
	}
	return groupId, nil
}

func (s *globalLimitService) GlobalLimitRequire(ctx context.Context, req v1.GlobalLimitRequest) (res v1.GlobalLimitRequireResponse, err error) {
	res.ExpiredAt, err = s.duedate.NextDueDate(ctx, req.Uid, req.HostId)
	if err != nil {
		return v1.GlobalLimitRequireResponse{}, err
	}
	configCount, err := s.host.GetGlobalLimitConfig(ctx, req.HostId)
	if err != nil {
		return v1.GlobalLimitRequireResponse{}, fmt.Errorf("获取配置限制失败: %w", err)
	}

	bpsInt, err := strconv.Atoi(configCount.Bps)
	if err != nil {
		return v1.GlobalLimitRequireResponse{}, err
	}
	resultFloat := float64(bpsInt) / 2.0 / 8.0
	res.Bps = strconv.FormatFloat( resultFloat, 'f', -1, 64) + "M"

	res.MaxBytesMonth = configCount.MaxBytesMonth
	res.Operator = configCount.Operator
	res.IpCount = configCount.IpCount
	domain, err := s.hostRep.GetDomainById(ctx, req.HostId)
	if err != nil {
		return v1.GlobalLimitRequireResponse{}, err
	}
	userInfo,err := s.globalLimitRepository.GetUserInfo(ctx, int64(req.Uid))
	if err != nil {
		return v1.GlobalLimitRequireResponse{}, err
	}
	res.GlobalLimitName = strconv.Itoa(req.Uid) + "_" + userInfo.Username + "_" + strconv.Itoa(req.HostId) + "_" + domain
	res.HostName, err = s.globalLimitRepository.GetHostName(ctx, int64(req.HostId))
	if err != nil {
		return v1.GlobalLimitRequireResponse{}, err
	}
	return res, nil
}

func (s *globalLimitService) GetGlobalLimit(ctx context.Context, id int64) (*model.GlobalLimit, error) {
	return s.globalLimitRepository.GetGlobalLimit(ctx, id)
}

func (s *globalLimitService) ConversionTime(ctx context.Context,req string) (string, error)  {
	// 2. 将字符串解析成 time.Time 对象
	// time.Parse 会根据你提供的布局来理解输入的字符串
	t, err := time.Parse("2006-01-02 15:04:05", req)
	if err != nil {
		// 如果输入的字符串格式和布局不匹配，这里会报错
		return "", fmt.Errorf("输入的字符串格式和布局不匹配 %w", err)
	}
	// 3. 定义新的输出格式 "YYYY-MM-DD"
	outputLayout := "2006-01-02"
	// 4. 将 time.Time 对象格式化为新的字符串
	outputTimeStr := t.Format(outputLayout)
	return outputTimeStr, nil
}

func (s *globalLimitService) ConversionTimeUnix(ctx context.Context,req string) (int64, error)  {
	t, err := time.Parse("2006-01-02 15:04:05", req)
	if err != nil {
		return 0, fmt.Errorf("输入的字符串格式和布局不匹配 %w", err)
	}
	expiredAt := t.Unix()
	return expiredAt, nil
}
func (s *globalLimitService) AddGlobalLimit(ctx context.Context, req v1.GlobalLimitRequest) error {
	isExist, err := s.globalLimitRepository.IsGlobalLimitExistByHostId(ctx, int64(req.HostId))
	if err != nil {
		return err
	}
	if isExist {
		return fmt.Errorf("配置限制已存在")
	}
	require, err := s.GlobalLimitRequire(ctx, req)
	if err != nil {
		return err
	}

	g, gCtx := errgroup.WithContext(ctx)
	var gatewayGroupId int
	var userId int64
	var groupId int64
	g.Go(func() error {
		res, e := s.gateWayGroupRep.GetGatewayGroupWhereHostIdNull(gCtx, require.Operator, require.IpCount)
		if e != nil {
			return fmt.Errorf("获取网关组失败: %w", e)
		}
		if res == 0 {
			return fmt.Errorf("获取网关组失败")
		}
		gatewayGroupId = res
		return nil
	})



	g.Go(func() error {
		res, e := s.GetCdnUserId(gCtx, int64(req.Uid))
		if e != nil {
			return fmt.Errorf("获取cdn用户失败: %w", e)
		}
		if res == 0 {
			return fmt.Errorf("获取cdn用户失败")
		}
		userId = res
		return nil
	})

	g.Go(func() error {
		res, e := s.AddGroupId(gCtx, require.GlobalLimitName)
		if e != nil {
			return fmt.Errorf("创建规则分组失败: %w", e)
		}
		if res == 0 {
			return fmt.Errorf("创建规则分组失败")
		}
		groupId = res
		return nil
	})

	if err = g.Wait(); err != nil {
		return err
	}


	outputTimeStr, err := s.ConversionTime(ctx, require.ExpiredAt)
	if err != nil {
		return err
	}

	ruleId, err := s.cdnService.BindPlan(ctx, v1.Plan{
		UserId:    userId,
		PlanId: 	4,
		DayTo:     outputTimeStr,
		Name:      require.GlobalLimitName,
		IsFree:    true,
		Period:    "monthly",
		CountPeriod: 1,
		PeriodDayTo: outputTimeStr,
	})
	if err != nil {
		return err
	}
	if ruleId == 0 {
		return fmt.Errorf("分配套餐失败")
	}



	err = s.gateWayGroupRep.EditGatewayGroup(ctx, &model.GatewayGroup{
		Id: gatewayGroupId,
		HostId: req.HostId,
	})
	if err != nil {
		return err
	}

	expiredAt, err := s.ConversionTimeUnix(ctx, require.ExpiredAt)
	if err != nil {
		return err
	}
	err = s.globalLimitRepository.AddGlobalLimit(ctx, &model.GlobalLimit{
		HostId:         req.HostId,
		Uid:            req.Uid,
		Name:           require.GlobalLimitName,
		RuleId: 		int(ruleId),
		GroupId:        int(groupId),
		GatewayGroupId: gatewayGroupId,
		CdnUid:         int(userId),
		Comment:        req.Comment,
		ExpiredAt:      expiredAt,
	})
	if err != nil {
		return err
	}
	return nil
}

func (s *globalLimitService) EditGlobalLimit(ctx context.Context, req v1.GlobalLimitRequest) error {
	require, err := s.GlobalLimitRequire(ctx, req)
	if err != nil {
		return err
	}
	data, err :=  s.globalLimitRepository.GetGlobalLimitByHostId(ctx, int64(req.HostId))
	if err != nil {
		return err
	}
	outputTimeStr, err := s.ConversionTime(ctx, require.ExpiredAt)
	if err != nil {
		return err
	}
	err = s.cdnService.RenewPlan(ctx, v1.RenewalPlan{
		UserPlanId: int64(data.RuleId),
		DayTo:      outputTimeStr,
		Period:     "monthly",
		CountPeriod: 1,
		IsFree:     true,
		PeriodDayTo: outputTimeStr,
	})
	if err != nil {
		return err
	}

	expiredAt, err := s.ConversionTimeUnix(ctx, require.ExpiredAt)
	if err != nil {
		return err
	}
	if err := s.globalLimitRepository.UpdateGlobalLimitByHostId(ctx, &model.GlobalLimit{
		HostId:  req.HostId,
		Comment: req.Comment,
		ExpiredAt: expiredAt,
	}); err != nil {
		return err
	}
	return nil
}

func (s *globalLimitService) EditGlobalLimitBySnail(ctx context.Context, req v1.GlobalLimitEditRequest) error {
	configCount, err := s.host.GetGlobalLimitConfig(ctx, req.HostId)
	if err != nil {
		return fmt.Errorf("获取配置限制失败: %w", err)
	}
	data, err := s.globalLimitRepository.GetGlobalLimitByHostId(ctx, int64(req.HostId))
	if err != nil {
		return err
	}
	t := time.Unix(req.ExpiredAt, 0)
	expiredAt := t.Format("2006-01-02 15:04:05")
	formData := map[string]interface{}{
		"tag":             data.Name,
		"bps":             configCount.Bps,
		"max_bytes_month": configCount.MaxBytesMonth,
		"expired_at":      expiredAt,
	}

	respBody, err := s.required.SendForm(ctx, "admin/info/waf_common_limit/edit?&__goadmin_edit_pk="+strconv.Itoa(req.RuleId), "admin/edit/waf_common_limit", formData)

	if err != nil {
		return err
	}
	if respBody == nil {
		return nil
	}
	return nil

}

func (s *globalLimitService) DeleteGlobalLimit(ctx context.Context, req v1.GlobalLimitRequest) error {
	if err := s.globalLimitRepository.DeleteGlobalLimitByHostId(ctx, int64(req.HostId)); err != nil {
		return err
	}
	return nil
}
