package actions

import (
	"fmt"
	"gorm.io/gorm"
	"log/slog"
	"zzman/clients/jd"
	"zzman/model"
)

// Sync 同步城市节点数据
//
// 数据库中保留云端已经移除的节点是有必要的，因为在节点轮换时需要保证能够从正确的位置开始，
// 而其之前所用的节点可能已经被云端移除，因此需要保留这些节点的记录以保持正确的轮换顺位。
func Sync() (err error) {
	slog.Info("开始同步城市节点数据")

	// 获取需要更新的城市列表
	cities, err := FindCities(model.DB)
	if err != nil {
		return fmt.Errorf("获取所有城市失败: %w", err)
	}
	slog.Info("成功获取城市列表", slog.Int("城市数量", len(cities)))

	// 获取所有城市的节点数据
	for i, city := range cities {
		slog.Info("正在同步城市", slog.String("城市", city.Name), slog.String("哈希", city.Hash))

		// 新节点信息
		resp, err := jd.EdgeDevice(jd.EdgeDeviceReq{
			Geo:    city.Hash,
			Offset: 0,
			Num:    1000000,
		})
		if err != nil {
			return fmt.Errorf("获取城市 %s:%s 的边缘设备失败: %w", city.Name, city.Hash, err)
		}
		var newEdges = resp.Edges
		slog.Info("获取节点数据完成", slog.String("城市", city.Name), slog.Int("节点数量", len(newEdges)))

		err = model.DB.Transaction(func(tx *gorm.DB) error {
			// 旧节点信息
			oldEdges, err := FindEdgesByCity(tx, city.Id)
			if err != nil {
				return fmt.Errorf("获取所有边缘节点MAC地址失败: %w", err)
			}
			var oldEdgesMacSet = make(map[string]struct{}, len(oldEdges))
			for _, edge := range oldEdges {
				oldEdgesMacSet[edge.Macaddr] = struct{}{}
			}

			// 对比并更新节点信息（全量更新）
			var edgeSaves = make([]model.Edge, 0)
			for _, edge := range newEdges {
				edgeSaves = append(edgeSaves, model.Edge{
					CityId:  city.Id,
					Macaddr: edge.Macaddr,
					Public:  edge.Public,
					Isp:     edge.Isp,
					Single:  edge.Single,
					Sole:    edge.Sole,
					Arch:    edge.Arch,
					Online:  edge.Online,
					Active:  true,
				})
				delete(oldEdgesMacSet, edge.Macaddr)
			}

			// 更新现有数据
			if err = SaveEdges(tx, edgeSaves); err != nil {
				return fmt.Errorf("批量保存边缘节点失败: %w", err)
			}
			// 删除旧节点信息
			var oldEdgesMacs = make([]string, 0, len(oldEdgesMacSet))
			for mac := range oldEdgesMacSet {
				oldEdgesMacs = append(oldEdgesMacs, mac)
			}
			if err = DisableEdgesByMacs(tx, oldEdgesMacs); err != nil {
				return fmt.Errorf("通过MAC地址删除边缘节点失败: %w", err)
			}

			slog.Info("城市同步完成",
				slog.Int("新节点", len(newEdges)),
				slog.Int("旧节点", len(oldEdges)),
				slog.Int("移除", len(oldEdgesMacs)),
				slog.Int("同步", len(edgeSaves)),
				slog.String("进度", fmt.Sprintf("%d/%d", i+1, len(cities))),
			)
			return nil
		})
		if err != nil {
			return fmt.Errorf("城市 %s 事务处理失败: %w", city.Name, err)
		}

	}

	return nil
}
