// Package appcode 集中处理多 App 租户键的归一化和 context 透传。
package appcode

import (
	"context"
	"crypto/rand"
	"fmt"
	"strings"
)

const (
	// Default 是当前开发阶段的种子 App；旧请求未显式携带 app_code 时统一落到 lalu。
	Default = "lalu"
)

type contextKey struct{}

// Normalize 把外部输入收敛成数据库使用的短 app_code。
func Normalize(value string) string {
	value = strings.ToLower(strings.TrimSpace(value))
	if value == "" {
		// 开发阶段不保留历史兼容表结构，但测试和后台任务仍需要一个稳定默认租户。
		return Default
	}

	return value
}

// WithContext 把 app_code 写入 context，供 service/repository 在不扩散方法签名的情况下取用。
func WithContext(ctx context.Context, value string) context.Context {
	return context.WithValue(ctx, contextKey{}, Normalize(value))
}

// FromContext 从 context 读取 app_code；缺失时返回当前开发默认 App。
func FromContext(ctx context.Context) string {
	if ctx == nil {
		return Default
	}
	value, _ := ctx.Value(contextKey{}).(string)
	return Normalize(value)
}

// NewScopedID 生成对客户端暴露的 App 长 ID。
// 格式固定为 app_code + "_" + uuid，保证同一个 ID 可直接作为用户长 ID、房间 ID 和腾讯 IM/RTC 字符串标识。
func NewScopedID(value string) string {
	app := Normalize(value)
	var bytes [16]byte
	if _, err := rand.Read(bytes[:]); err != nil {
		// crypto/rand 失败极少见；这里退化为全 0 仍保留格式，后续数据库唯一键会兜底冲突。
		return fmt.Sprintf("%s_00000000-0000-4000-8000-000000000000", app)
	}

	bytes[6] = (bytes[6] & 0x0f) | 0x40
	bytes[8] = (bytes[8] & 0x3f) | 0x80

	return fmt.Sprintf("%s_%08x-%04x-%04x-%04x-%012x",
		app,
		bytes[0:4],
		bytes[4:6],
		bytes[6:8],
		bytes[8:10],
		bytes[10:16],
	)
}
