// Package info loads and serves auxiliary data: backlink scores, adjustment
// table, and blocked query words.
// info 包负责加载和管理辅助数据：繁荣表（反向链接分数）、调整表（人工权重调整）和屏蔽词表。
package info

import (
	"encoding/json"  // JSON 反序列化
	"math"          // 对数运算（Log2）
	"os"            // 文件读取
	"path/filepath" // 路径拼接
	"strings"       // 字符串操作
	"sync"          // 读写锁
)

// Service 管理繁荣表、调整表和屏蔽词表，并提供只读快照。
type Service struct {
	mu           sync.RWMutex
	prosperMap   map[string]float64   // 繁荣表：域名 → 归一化反向链接分数
	adjustTable  map[string]float64   // 调整表：主机名 → 人工权重倍数（默认 1.0）
	blockedWords map[string]bool       // 屏蔽词集合：搜索时直接过滤
	storagePath  string               // 存储根目录路径
}

// New 创建并加载 info Service，从 storagePath 目录读取数据文件。
func New(storagePath string) *Service {
	s := &Service{storagePath: storagePath}
	s.Reload()
	return s
}

// Reload 从磁盘重新加载所有数据文件（支持热更新配置）。
func (s *Service) Reload() {
	s.mu.Lock()
	defer s.mu.Unlock()
	s.prosperMap = loadProsperMap(s.storagePath)
	s.adjustTable = loadAdjustTable()
	s.blockedWords = loadBlockedWords()
}

// Prosper 返回指定 URL 的繁荣分数（对其所有路径段累计计算）。
// 分数越高表示该域名越"有价值"（反向链接越多）。
func (s *Service) Prosper(rawURL string) float64 {
	s.mu.RLock()
	defer s.mu.RUnlock()
	return prosperFor(rawURL, s.prosperMap)
}

// ProsperMap 返回繁荣表的完整只读快照（深拷贝）。
// 供爬虫调度算法使用。
func (s *Service) ProsperMap() map[string]float64 {
	s.mu.RLock()
	defer s.mu.RUnlock()
	out := make(map[string]float64, len(s.prosperMap))
	for k, v := range s.prosperMap {
		out[k] = v
	}
	return out
}

// Adjust 返回指定主机名的人工权重倍数（默认 1.0）。
// 允许管理员通过调整表提升或降低某些域名的爬取/搜索优先级。
func (s *Service) Adjust(host string) float64 {
	s.mu.RLock()
	defer s.mu.RUnlock()
	if v, ok := s.adjustTable[host]; ok {
		return v
	}
	return 1.0
}

// IsBlocked 判断某词是否在屏蔽词列表中（搜索时不返回含该词的结果）。
func (s *Service) IsBlocked(word string) bool {
	s.mu.RLock()
	defer s.mu.RUnlock()
	return s.blockedWords[word]
}

// ---- 数据加载函数 ----

// backlinkBaseline 繁荣表归一化的基准值（用于将原始链接数映射到固定区间）。
const backlinkBaseline = 200000.0

// loadProsperMap 从 storage/prosper.json 加载繁荣表，并进行归一化和域名树传播。
func loadProsperMap(storagePath string) map[string]float64 {
	path := filepath.Join(storagePath, "prosper.json")
	f, err := os.Open(path)
	if err != nil {
		return map[string]float64{}
	}
	defer f.Close()
	var raw map[string]float64
	if err := json.NewDecoder(f).Decode(&raw); err != nil {
		return map[string]float64{}
	}
	return normalise(raw)
}

// normalise 对繁荣表进行归一化，并执行域名树传播。
// 归一化：将所有顶级域名的分数总和缩放到 backlinkBaseline。
// 传播：子域名分数向上传播到父域名（父域名分数不低于任何子域名）。
func normalise(d map[string]float64) map[string]float64 {
	// 计算顶级域名（不含 "/"）的分数总和
	total := 0.0
	for k, v := range d {
		if !strings.Contains(k, "/") {
			total += v
		}
	}
	if total == 0 {
		return d
	}
	// 按总和归一化
	factor := backlinkBaseline / total
	out := make(map[string]float64, len(d))
	for k, v := range d {
		out[k] = v * factor
	}
	// 域名树传播：子域名分数 ≥ 父域名分数
	for k, v := range out {
		now := k
		for {
			idx := strings.Index(now, ".")
			if idx < 0 {
				break
			}
			now = now[idx+1:] // 上移一级
			if cur, ok := out[now]; ok && cur < v {
				out[now] = v // 父域名分数不低于子域名
			} else if !ok {
				break
			}
		}
	}
	return out
}

// loadAdjustTable 从 data/adjust.json 加载人工调整表（主机名 → 权重倍数）。
// 文件不存在时返回空 map（所有域名权重为默认 1.0）。
func loadAdjustTable() map[string]float64 {
	f, err := os.Open(filepath.Join("data", "adjust.json"))
	if err != nil {
		return map[string]float64{}
	}
	defer f.Close()
	var m map[string]float64
	json.NewDecoder(f).Decode(&m)
	return m
}

// loadBlockedWords 从 data/blocked_words.json 加载屏蔽词列表。
// 文件不存在时返回空集合。
func loadBlockedWords() map[string]bool {
	f, err := os.Open(filepath.Join("data", "blocked_words.json"))
	if err != nil {
		return map[string]bool{}
	}
	defer f.Close()
	var words []string
	json.NewDecoder(f).Decode(&words)
	m := make(map[string]bool, len(words))
	for _, w := range words {
		m[w] = true
	}
	return m
}

// prosperFor 对 URL 按路径段分解查询繁荣表，计算综合繁荣分数。
// 分数计算：对每段取 Log2 变换后累加，返回值范围约 [0.1, +∞)。
func prosperFor(rawURL string, pm map[string]float64) float64 {
	segments := decomposeURL(rawURL)
	s := 0.0
	for _, seg := range segments {
		t, ok := pm[seg]
		if !ok {
			t = 0
		}
		l := 0.0
		if t > 0 {
			l = math.Log2(2+t*2) - 1 // Log2(2+2t)-1，t=0 时为 0，随 t 增大而增大
		}
		if s == 0 {
			if l == 0 {
				return 0
			}
			s = l
		} else {
			s = l + math.Log((s-l)/2+1) // 累加并衰减
		}
	}
	if s > 0 {
		return 0.1 + s
	}
	return 0
}

// decomposeURL 将 URL 分解为递增的路径段。
// 例如："https://zh.wikipedia.org/wiki/Go" → ["zh.wikipedia.org", "zh.wikipedia.org/wiki", "zh.wikipedia.org/wiki/Go"]。
// 用于按从泛到精的顺序查繁荣表。
func decomposeURL(rawURL string) []string {
	u := strings.ToLower(rawURL)
	if strings.HasPrefix(u, "https://") {
		u = u[8:]
	} else if strings.HasPrefix(u, "http://") {
		u = u[7:]
	} else {
		return nil
	}
	u = strings.ReplaceAll(u, "?", "/")  // 查询参数转路径
	u = strings.ReplaceAll(u, "#", "/")  // 锚点转路径
	u = strings.TrimRight(u, "/")
	// 过滤无效格式
	if u == "" || u[0] == '/' || u[0] == '%' || u[0] == ' ' {
		return nil
	}
	parts := strings.Split(u, "/")
	var out []string
	current := parts[0]
	out = append(out, current) // 第一段：顶级域名
	for _, p := range parts[1:] {
		current = current + "/" + p // 逐步拼接路径段
		out = append(out, current)
	}
	return out
}
