package gameShield

import (
	"context"
	"fmt"
	"github.com/AlekSi/pointer"
	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/go-nunu/nunu-layout-advanced/internal/service"
	"github.com/jinzhu/copier"
	"github.com/spf13/cast"
	"github.com/spf13/viper"
	"strconv"
	"strings"
)

type GameShieldBackendService interface {
	GetGameShieldBackend(ctx context.Context, id int64) (*model.GameShieldBackend, error)
	GameShieldBackend(ctx context.Context, req *v1.GameShieldBackendArrayRequest) (string, int, error)
	AddGameShieldBackend(ctx context.Context, req *v1.GameShieldBackendArrayRequest) (string, error)
	EditGameShieldBackend(ctx context.Context, req *v1.GameShieldBackendArrayRequest) (string, error)
	DeleteGameShieldBackend(ctx context.Context, req *v1.DelGameShieldBackendRequest) (string, error)
	GetGameShieldRequired(ctx context.Context, req *v1.GameShieldBackendArrayRequest) (*v1.GetGameShieldRequiredResponse, int, error)
	ReplacementSourceMachineIp(ctx context.Context, req *v1.ReplacementSourceMachineIpRequest) error
}

func NewGameShieldBackendService(
	service *service.Service,
	gameShieldBackendRepository repository.GameShieldBackendRepository,
	gameShieldRepository repository.GameShieldRepository,
	crawlerService service.CrawlerService,
	gameShieldPublicIpService service.GameShieldPublicIpService,
	duedate service.DuedateService,
	formatter service.FormatterService,
	parser service.ParserService,
	required service.RequiredService,
	conf *viper.Viper,
	shieldService GameShieldService,
	hostService service.HostService,
) GameShieldBackendService {
	return &gameShieldBackendService{
		Service:                     service,
		gameShieldBackendRepository: gameShieldBackendRepository,
		gameShieldRepository:        gameShieldRepository,
		crawlerService:              crawlerService,
		gameShieldPublicIpService:   gameShieldPublicIpService,
		duedate:                     duedate,
		formatter:                   formatter,
		parser:                      parser,
		required:                    required,
		Url:                         conf.GetString("crawler.Url"),
		shieldService:               shieldService,
		hostService:                 hostService,
	}
}

type gameShieldBackendService struct {
	*service.Service
	gameShieldBackendRepository repository.GameShieldBackendRepository
	crawlerService              service.CrawlerService
	gameShieldRepository        repository.GameShieldRepository
	gameShieldPublicIpService   service.GameShieldPublicIpService
	duedate                     service.DuedateService
	formatter                   service.FormatterService
	Url                         string
	parser                      service.ParserService
	required      service.RequiredService
	shieldService GameShieldService
	hostService   service.HostService
}

func (s *gameShieldBackendService) GetGameShieldRequired(ctx context.Context, req *v1.GameShieldBackendArrayRequest) (*v1.GetGameShieldRequiredResponse, int, error) {
	var res v1.GetGameShieldRequiredResponse
	var err error
	var count int
	if req.Uid == 0 {
		return nil, 0, fmt.Errorf("uid is required")
	}
	res.ExpiredAt, err = s.duedate.NextDueDate(ctx, req.Uid, req.HostId)
	if err != nil {
		return nil, 0, err
	}
	gameShield, err := s.gameShieldRepository.GetGameShieldByHostId(ctx, req.HostId)
	if err != nil {
		return nil, 0, err
	}
	res.RuleId = gameShield.RuleId
	oldBackend, err := s.gameShieldBackendRepository.GetGameShieldBackendByHostId(ctx, req.HostId)
	if err != nil {
		return nil, 0, err
	}
	if len(oldBackend) != 0 {
		count = oldBackend[0].KeySort
	}
	OldBackend, err := s.formatter.OldFormat(ctx, &oldBackend)
	res.Backend, err = s.formatter.FormatBackendData(ctx, req, OldBackend, count)
	if err != nil {
		return nil, 0, err
	}
	res.Cookie, err = s.crawlerService.GetLoginCookie(ctx)
	if err != nil {
		return nil, 0, err
	}
	res.DunName = gameShield.DunName
	return &res, count, nil
}
func (s *gameShieldBackendService) GetGameShieldBackend(ctx context.Context, id int64) (*model.GameShieldBackend, error) {
	res, err := s.gameShieldBackendRepository.GetGameShieldBackendById(ctx, id)
	if err != nil {
		return nil, err
	}
	return res, nil
}

func (s *gameShieldBackendService) GameShieldBackend(ctx context.Context, req *v1.GameShieldBackendArrayRequest) (string, int, error) {
	require, count, err := s.GetGameShieldRequired(ctx, req)
	if err != nil {
		return "", 0, err
	}
	tokenUrl := s.Url + "admin/info/rule/edit?&__goadmin_edit_pk=" + strconv.Itoa(require.RuleId) + "_" + require.DunName
	tokens, err := s.crawlerService.GetFormTokens(ctx, tokenUrl, require.Cookie)
	if err != nil {
		return "", 0, err
	}
	configCount, err := s.hostService.GetGameShieldConfig(ctx, req.HostId)
	if err != nil {
		return "", 0, fmt.Errorf("获取配置限制失败: %w", err)
	}
	formData := map[string]interface{}{
		"app_name":             require.DunName,
		"gateway_group_id":     2,	// TODO: 临时写死
		"backend":              require.Backend,
		"rule_id":              require.RuleId,
		"expired_at":           require.ExpiredAt,
		"max_device_count":     configCount.OnlineDevicesCount,
		"sdk_args":             "--max-bandwidth 1000K",
		"__go_admin_previous_": tokens["previous"],
		"__go_admin_t_":        tokens["t"],
	}
	sendUrl := s.Url + "admin/edit/rule"
	respBody, err := s.crawlerService.SendFormData(ctx, sendUrl, require.Cookie, formData)
	if err != nil {
		return "", 0, err
	}
	// 解析响应内容中的 alert 消息
	res, err := s.parser.ParseAlert(string(respBody))
	if err != nil {
		return "", 0, err
	}
	if res != "" {
		return "", 0, fmt.Errorf(res)
	}
	KeyAndField, err := s.required.GetKeyAndField(ctx, require.DunName, "rule_id")
	if err != nil {
		return "", 0, err
	}
	timeBase, err := s.gameShieldRepository.GetGameShieldNextduedate(ctx, int64(req.Uid), req.HostId)
	if err != nil {
		return "", 0, err
	}
	timestampSec, err := strconv.ParseInt(timeBase, 10, 64)
	if err != nil {
		return "", 0, err
	}
	if err := s.gameShieldRepository.UpdateGameShieldByHostId(ctx, &model.GameShield{HostId: req.HostId, Key: KeyAndField.Key, ExpireTime: timestampSec}); err != nil {
		return "", 0, err
	}
	return res, count, nil
}

func (s *gameShieldBackendService) AddGameShieldBackend(ctx context.Context, req *v1.GameShieldBackendArrayRequest) (string, error) {
	res, count, err := s.GameShieldBackend(ctx, req)
	if err != nil {
		return "", err
	}
	saveData, err := s.formatter.TidyFormatBackendData(ctx, req, count)
	if err != nil {
		return "", err
	}
	if err := s.SaveGameShieldBackend(ctx, saveData, req.HostId); err != nil {
		return "", err
	}
	return res, nil
}
func (s *gameShieldBackendService) EditGameShieldBackend(ctx context.Context, req *v1.GameShieldBackendArrayRequest) (string, error) {
	// 1. 获取当前所有数据库记录
	currentData, err := s.gameShieldBackendRepository.GetGameShieldBackendByHostId(ctx, req.HostId)
	if err != nil {
		return "", fmt.Errorf("获取当前配置失败: %w", err)
	}
	// 2. 创建当前记录的副本用于模拟修改
	simulatedData := make([]model.GameShieldBackend, len(currentData))
	copy(simulatedData, currentData)

	// 3. 创建ID到索引的映射，方便查找和修改
	idToIndex := make(map[int64]int)
	for i, item := range simulatedData {
		idToIndex[int64(item.Id)] = i
	}

	// 4. 在副本上应用修改
	for _, v := range req.Items {
		if v.Id == 0 {
			return "", fmt.Errorf("id 不能为空")
		}

		// 查找对应记录
		idx, exists := idToIndex[int64(v.Id)]
		if !exists {
			return "", fmt.Errorf("ID为%d的记录不存在", v.Id)
		}

		// 更新记录（只更新需要修改的字段）
		simulatedData[idx].SourceMachineIP = v.SourceMachineIP
		simulatedData[idx].ConnectPort = v.ConnectPort
		simulatedData[idx].Protocol = v.Protocol
		simulatedData[idx].SdkPort = v.SdkPort
		simulatedData[idx].SdkIp = v.SdkIp
		simulatedData[idx].Type = v.Type
		//获取指针的值
		simulatedData[idx].MaxBandwidth = pointer.GetInt(v.MaxBandwidth)
	}
	// 5. 使用模拟修改后的数据进行验证
	// 转换数据格式
	simulatedBackend, err := s.formatter.OldFormat(ctx, &simulatedData)
	if err != nil {
		return "", fmt.Errorf("格式化模拟数据失败: %w", err)
	}

	// 验证修改后的配置
	err = s.formatter.ValidateBackendData(ctx, simulatedBackend, req.HostId)
	if err != nil {
		return "", fmt.Errorf("验证失败: %w", err)
	}

	// 6. 验证通过，执行实际的数据库修改
	for _, v := range req.Items {
		if err := s.gameShieldBackendRepository.EditGameShieldBackend(ctx, &v); err != nil {
			return "", fmt.Errorf("修改数据失败(ID:%d): %w", v.Id, err)
		}
	}

	// 7. 更新远程配置
	res, _, err := s.GameShieldBackend(ctx, &v1.GameShieldBackendArrayRequest{
		HostId: req.HostId,
		Uid:    req.Uid,
		Items:  nil,
	})
	if err != nil {
		return "", fmt.Errorf("更新配置失败: %w", err)
	}

	return res, nil
}
func (s *gameShieldBackendService) DeleteGameShieldBackend(ctx context.Context, req *v1.DelGameShieldBackendRequest) (string, error) {
	for _, v := range req.Ids {
		if err := s.gameShieldBackendRepository.DeleteGameShieldBackend(ctx, v, req.HostId); err != nil {
			return "", err
		}
	}
	res, _, err := s.GameShieldBackend(ctx, &v1.GameShieldBackendArrayRequest{HostId: req.HostId, Uid: req.Uid, Items: nil})
	if err != nil {
		return "", err
	}
	return res, nil
}

func (s *gameShieldBackendService) SaveGameShieldBackend(ctx context.Context, req map[string]v1.SendGameShieldBackend, hostId int) error {
	for k, v := range req {
		parts := strings.Split(v.Addr[0], ":")
		keyName := strings.Split(k, "key")[1]
		key, err := strconv.Atoi(keyName)
		if err != nil {
			return err
		}
		if v.Type != "pc" {
			v.SdkIp = "127.0.0.1"
		}
		if v.MaxBandwidth == "12.5m" {
			v.MaxBandwidth = "1"
		} else {
			v.MaxBandwidth = "0"
		}
		if v.Protocol != "http" {
			v.Host = ""
		}
		if err := s.gameShieldBackendRepository.AddGameShieldBackend(ctx,
			&model.GameShieldBackend{
				HostId:          hostId,
				KeySort:         key,
				SourceMachineIP: parts[0],
				Protocol:        v.Protocol,
				ProxyAddr:       v.ProxyAddr,
				ConnectPort:     parts[1],
				SdkIp:           v.SdkIp,
				SdkPort:         strconv.Itoa(v.SdkPort),
				Type:            v.Type,
				MaxBandwidth:    cast.ToInt(v.MaxBandwidth),
				Host:            v.Host,
				Remark:          v.Remark,
			}); err != nil {
			return err
		}
	}

	return nil
}

// ReplacementSourceMachineIp 替换源机ip
func (s *gameShieldBackendService) ReplacementSourceMachineIp(ctx context.Context, req *v1.ReplacementSourceMachineIpRequest) error {
	var data []v1.GameShieldBackendRequest
	var baseData []model.GameShieldBackend
	var err error
	if len(req.OldSourceMachineIp) != 0 {
		baseData, err = s.gameShieldBackendRepository.GetGameShieldBackendByHostIdAndSourceMachineIp(ctx, req.HostId, req.OldSourceMachineIp)
		if err != nil {
			return err
		}
		for i := range baseData {
			baseData[i].SourceMachineIP = req.NewSourceMachineIp
		}
	}
	if len(req.OldSourceMachineIp) == 0 {
		baseData, err = s.gameShieldBackendRepository.GetGameShieldBackendByHostId(ctx, req.HostId)
		if err != nil {
			return err
		}
		for i := range baseData {
			baseData[i].SourceMachineIP = req.NewSourceMachineIp
		}
	}
	err = copier.Copy(&data, &baseData)
	if err != nil {
		return err
	}
	_, err = s.EditGameShieldBackend(ctx, &v1.GameShieldBackendArrayRequest{
		Items:  data,
		HostId: req.HostId,
		Uid:    req.Uid,
	})
	if err != nil {
		return err
	}
	return nil
}