// Package httpapi 路由装配。各业务模块在各自包内实现 handler，这里只负责挂载，
// 并按配置在真实外部依赖（微信/支付/OSS/ASR）与 mock 之间切换。
package httpapi

import (
	"context"
	"log/slog"
	"net/http"

	"github.com/gin-gonic/gin"
	"github.com/redis/go-redis/v9"
	"gorm.io/gorm"

	"dudu/server/internal/asr"
	"dudu/server/internal/auth"
	"dudu/server/internal/billing"
	"dudu/server/internal/config"
	"dudu/server/internal/feedback"
	"dudu/server/internal/gateway"
	"dudu/server/internal/quota"
	"dudu/server/internal/telemetry"
	"dudu/server/internal/user"
)

type Deps struct {
	Cfg   config.Config
	DB    *gorm.DB
	RDB   *redis.Client
	JWT   *auth.JWT
	Quota *quota.Manager
}

// pickProvider 按配置选择 ASR Provider（17E）。ASR_PROVIDER=gummy 且 DASHSCOPE_API_KEY
// 就绪时装配真实 gummy；缺 key 则告警并降级 mock。返回值 kind 为装配结果（"gummy"/"mock"）。
func pickProvider(cfg config.Config) (asr.Provider, string) {
	switch cfg.ASRProvider {
	case "gummy":
		if cfg.DashScopeAPIKey != "" {
			return asr.NewGummy(cfg.DashScopeAPIKey), "gummy"
		}
		slog.Warn("ASR_PROVIDER=gummy but DASHSCOPE_API_KEY empty, falling back to mock")
		return asr.NewMock(), "mock"
	default:
		return asr.NewMock(), "mock"
	}
}

// pickWechat 微信凭证未配置（#2A 申请中）时使用 mock。返回 kind="mock"（真实实现就绪前恒为 mock）。
func pickWechat(cfg config.Config) (auth.WechatClient, string) {
	if cfg.WechatWebAppID == "" {
		return auth.MockWechat{}, "mock"
	}
	return auth.MockWechat{}, "mock" // TODO(5C-真实): 开放平台实现，凭证就绪后替换
}

// pickMailer SMTP 未配置时使用 mock（验证码打日志，供联调）。返回 kind。
// ⚠️ 上线前必须配置真实 SMTP，否则邮箱登录形同虚设。
func pickMailer(cfg config.Config) (auth.Mailer, string) {
	if cfg.SMTPHost == "" {
		return auth.MockMailer{}, "mock"
	}
	return auth.MockMailer{}, "mock" // TODO(邮箱登录-真实): SMTP 实现，凭证就绪后替换
}

// pickPay 商户号未配置（#2B 申请中）时使用 mock。返回 kind="mock"。
// ⚠️⚠️ 上线前必须替换为真实 PayClient！MockPay 不验签，/v1/pay/notify 等于公开充值接口。
func pickPay(cfg config.Config) (billing.PayClient, string) {
	if cfg.WxPayMchID == "" {
		return billing.MockPay{}, "mock"
	}
	// TODO(7D-真实)【上线阻断项】: wechatpay-go 实现 + APIv3 验签，商户号就绪后替换，否则充值接口可被伪造。
	return billing.MockPay{}, "mock"
}

// pickStorage OSS 未配置时图片落本地盘。返回 kind="local"（OSS 就绪后改为 "oss"）。
func pickStorage(cfg config.Config) (feedback.Storage, string) {
	if cfg.OSSBucket == "" {
		return feedback.LocalStorage{Dir: "var/uploads"}, "local"
	}
	return feedback.LocalStorage{Dir: "var/uploads"}, "local" // TODO(8C-真实): OSS 实现
}

func Register(r *gin.Engine, d *Deps) {
	r.GET("/healthz", func(c *gin.Context) { c.JSON(http.StatusOK, gin.H{"ok": true}) })

	provider, providerKind := pickProvider(d.Cfg)
	wechat, wechatKind := pickWechat(d.Cfg)
	pay, payKind := pickPay(d.Cfg)
	storage, storageKind := pickStorage(d.Cfg)
	mailer, mailerKind := pickMailer(d.Cfg)

	// 启动时打印各外部依赖的装配结果（real/mock），便于部署核对（17E）。
	slog.Info("dependency assembly",
		"asr_provider", providerKind, "wechat", wechatKind, "pay", payKind,
		"storage", storageKind, "mailer", mailerKind)

	if d.Cfg.EmailDevCode != "" {
		slog.Warn("⚠️ AUTH_EMAIL_DEV_CODE 已设置：邮箱验证码为固定值，任何人可登录任意邮箱账号——生产环境严禁")
	}
	authH := &auth.Handlers{DB: d.DB, RDB: d.RDB, JWT: d.JWT, Wechat: wechat, Mailer: mailer, DevCode: d.Cfg.EmailDevCode}
	billH := &billing.Handlers{DB: d.DB, Pay: pay, Quota: d.Quota}
	userH := &user.Handlers{DB: d.DB, Quota: d.Quota, AppVersions: d.Cfg.AppLatest}
	fbH := &feedback.Handlers{DB: d.DB, RDB: d.RDB, Storage: storage}
	telH := telemetry.New(d.DB, d.RDB)
	gwH := &gateway.Handler{Provider: provider, Quota: d.Quota, RDB: d.RDB}

	// 后台任务
	bg := context.Background()
	billH.StartWatchdog(bg)
	d.Quota.StartDailyReconcile(bg)
	telH.StartDailyAggregate(bg)

	v1 := r.Group("/v1")

	// 🔓 无需登录
	v1.POST("/auth/qr", authH.CreateQr)
	v1.GET("/auth/qr/:state", authH.PollQr)
	v1.GET("/auth/wechat/callback", authH.QrCallback)
	v1.POST("/auth/wechat", authH.MobileLogin)
	v1.POST("/auth/email/code", authH.EmailCode)
	v1.POST("/auth/email", authH.EmailLogin)
	v1.GET("/packs", billH.Packs)
	// ⚠️ 部署前必须切换到真实 PayClient（验签）！当前 MockPay 不验签，
	// /v1/pay/notify 等于一个无鉴权的公开充值接口（任意人可伪造支付成功回调充值）。
	// 上线前务必让 pickPay 返回带 APIv3 验签的真实实现（见 pickPay 的 TODO(7D-真实)）。
	v1.POST("/pay/notify", billH.Notify)
	// metrics/batch 可匿名上报；OptionalMiddleware 在带合法 JWT 时注入 user_id 以关联用户（17F）。
	v1.POST("/metrics/batch", d.JWT.OptionalMiddleware(), telH.Batch)
	v1.GET("/app/latest", userH.AppLatest)

	// 需登录
	authed := v1.Group("", d.JWT.Middleware())
	authed.POST("/auth/logout", authH.Logout)
	authed.GET("/me", userH.Me)
	authed.POST("/orders", billH.CreateOrder)
	authed.GET("/orders/:id", billH.OrderStatus)
	authed.POST("/feedback", fbH.Submit)
	authed.GET("/asr/stream", gwH.HandleWS)
}
