package agentd

import (
	"encoding/json"

	agentv1 "github.com/wangjia/pangolin/server/internal/pb/agentv1"
)

// renderSingboxConfig produces a complete sing-box SERVER config JSON for the node:
// a VLESS+REALITY inbound and a Hysteria2 inbound, each carrying one user per
// provisioned credential. REALITY users key on the dp_uuid; Hy2 users key on the
// derived password (DeriveHy2Password) — both from the same dp_uuid source.
//
// Only the opaque dp_uuid is ever written; no account identity touches the node.
//
// 用量统计走 v2ray_api StatsService(loopback gRPC):节点 sing-box 编入
// with_v2ray_api,stats.users 列出全部 dp_uuid → 每个用户独立的
// user>>>{dp_uuid}>>>traffic>>>uplink|downlink 计数器,agent 按用户精确读取
// (替代旧的 clash 节点总量分摊)。clash_api 仍保留作本地调试。
const (
	clashAPIAddr   = "127.0.0.1:19090"
	clashAPISecret = "pangolin-local-stats"
	v2rayAPIAddr   = "127.0.0.1:19091"
)

func renderSingboxConfig(creds []Cred, reality *agentv1.RealityInbound, hy2 *agentv1.Hy2Inbound, deriveKey string) ([]byte, error) {
	cfg := map[string]any{
		"log":       map[string]any{"level": "warn", "timestamp": true},
		"inbounds":  buildInbounds(creds, reality, hy2, deriveKey),
		"outbounds": []any{map[string]any{"type": "direct", "tag": "direct"}},
		"experimental": map[string]any{
			"clash_api": map[string]any{
				"external_controller": clashAPIAddr,
				"secret":              clashAPISecret,
			},
			"v2ray_api": map[string]any{
				"listen": v2rayAPIAddr,
				"stats": map[string]any{
					"enabled": true,
					"users":   statsUsers(creds),
				},
			},
		},
	}
	return json.MarshalIndent(cfg, "", "  ")
}

// statsUsers 收集所有去重 dp_uuid,供 v2ray_api stats.users 按用户开启流量计数器。
func statsUsers(creds []Cred) []string {
	seen := make(map[string]struct{}, len(creds))
	users := make([]string, 0, len(creds))
	for _, c := range creds {
		if c.DpUUID == "" {
			continue
		}
		if _, ok := seen[c.DpUUID]; ok {
			continue
		}
		seen[c.DpUUID] = struct{}{}
		users = append(users, c.DpUUID)
	}
	return users
}

func buildInbounds(creds []Cred, reality *agentv1.RealityInbound, hy2 *agentv1.Hy2Inbound, deriveKey string) []any {
	inbounds := make([]any, 0, 2)

	if reality != nil {
		users := make([]any, 0, len(creds))
		for _, c := range creds {
			if c.Protocol == agentv1.ProtocolReality || c.Protocol == agentv1.ProtocolBoth {
				flow := c.Flow
				if flow == "" {
					flow = DefaultFlow
				}
				users = append(users, map[string]any{
					"name": c.DpUUID,
					"uuid": c.DpUUID,
					"flow": flow,
				})
			}
		}
		realityTLS := map[string]any{
			"enabled":     true,
			"server_name": reality.ServerName,
			"reality": map[string]any{
				"enabled":     true,
				"private_key": reality.PrivateKey,
				"short_id":    []string{reality.ShortID},
				"handshake": map[string]any{
					"server":      reality.HandshakeServer,
					"server_port": reality.HandshakePort,
				},
			},
		}
		inbounds = append(inbounds, map[string]any{
			"type":        "vless",
			"tag":         "reality-in",
			"listen":      "::",
			"listen_port": reality.ListenPort,
			"users":       users,
			"tls":         realityTLS,
		})
	}

	if hy2 != nil {
		users := make([]any, 0, len(creds))
		for _, c := range creds {
			if c.Protocol == agentv1.ProtocolHy2 || c.Protocol == agentv1.ProtocolBoth {
				users = append(users, map[string]any{
					"name":     c.DpUUID,
					"password": DeriveHy2Password(c.DpUUID, deriveKey),
				})
			}
		}
		hy2In := map[string]any{
			"type":        "hysteria2",
			"tag":         "hy2-in",
			"listen":      "::",
			"listen_port": hy2.ListenPort,
			"users":       users,
			"tls": map[string]any{
				"enabled":          true,
				"alpn":             []string{"h3"},
				"certificate_path": hy2.CertPath,
				"key_path":         hy2.KeyPath,
			},
		}
		if hy2.Masquerade != "" {
			hy2In["masquerade"] = hy2.Masquerade
		}
		inbounds = append(inbounds, hy2In)
	}

	return inbounds
}
