// Code scaffolded by goctl. Safe to edit.
// goctl 1.10.1

package auth

import (
	"context"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"net/url"

	"sundynix-micro-go/app/auth/api/internal/svc"
	"sundynix-micro-go/app/auth/api/internal/types"
	plantPb "sundynix-micro-go/app/plant/rpc/plant"
	sysPb "sundynix-micro-go/app/system/rpc/system"

	"github.com/zeromicro/go-zero/core/logx"
)

// clientId 常量 — 新增小程序时在此添加
const (
	ClientIdPlant = "sundynix-plant" // 植物小程序
	// ClientIdRadio = "radio_mini" // 电台小程序（待接入）
)

// MiniAppAdditionalInfo 小程序客户端扩展信息（存储在 additional_info JSON 字段中）
type MiniAppAdditionalInfo struct {
	AppId     string `json:"appId"`
	AppSecret string `json:"appSecret"`
}

type MiniLoginLogic struct {
	logx.Logger
	ctx    context.Context
	svcCtx *svc.ServiceContext
}

// 微信小程序登录
func NewMiniLoginLogic(ctx context.Context, svcCtx *svc.ServiceContext) *MiniLoginLogic {
	return &MiniLoginLogic{
		Logger: logx.WithContext(ctx),
		ctx:    ctx,
		svcCtx: svcCtx,
	}
}

// WxCode2SessionResp 微信code2session响应
type WxCode2SessionResp struct {
	Openid     string `json:"openid"`
	SessionKey string `json:"session_key"`
	Unionid    string `json:"unionid"`
	Errcode    int    `json:"errcode"`
	Errmsg     string `json:"errmsg"`
}

func (l *MiniLoginLogic) MiniLogin(req *types.MiniLoginReq) (resp *types.LoginResp, err error) {
	// 1. 根据 clientId 查询客户端配置，从 additional_info 中动态读取 appId/appSecret
	clientResp, err := l.svcCtx.SystemRpc.GetClientById(l.ctx, &sysPb.GetClientByIdReq{
		ClientId: req.ClientId,
	})
	if err != nil {
		l.Errorf("[MiniLogin] 查询客户端失败: clientId=%s, err=%v", req.ClientId, err)
		return nil, fmt.Errorf("无效的客户端: %s", req.ClientId)
	}

	// 2. 解析 additional_info 获取微信凭证
	var miniInfo MiniAppAdditionalInfo
	if err = json.Unmarshal([]byte(clientResp.Client.AdditionalInfo), &miniInfo); err != nil {
		l.Errorf("[MiniLogin] 解析客户端 additional_info 失败: clientId=%s, info=%s, err=%v",
			req.ClientId, clientResp.Client.AdditionalInfo, err)
		return nil, fmt.Errorf("客户端配置格式错误，请检查 additional_info")
	}
	if miniInfo.AppId == "" || miniInfo.AppSecret == "" {
		return nil, fmt.Errorf("客户端 %s 未配置 appId 或 appSecret", req.ClientId)
	}

	// 3. 调用微信 code2session 接口
	params := url.Values{}
	params.Set("appid", miniInfo.AppId)
	params.Set("secret", miniInfo.AppSecret)
	params.Set("js_code", req.Code)
	params.Set("grant_type", "authorization_code")
	fullURL := "https://api.weixin.qq.com/sns/jscode2session?" + params.Encode()

	httpResp, err := http.Get(fullURL)
	if err != nil {
		l.Errorf("微信登录接口请求失败: %v", err)
		return nil, fmt.Errorf("微信登录接口请求失败")
	}
	defer httpResp.Body.Close()

	body, err := io.ReadAll(httpResp.Body)
	if err != nil {
		l.Errorf("读取微信接口响应失败: %v", err)
		return nil, fmt.Errorf("读取微信登录接口响应失败")
	}

	var wxResp WxCode2SessionResp
	if err = json.Unmarshal(body, &wxResp); err != nil {
		l.Errorf("解析微信接口响应失败: %v", err)
		return nil, fmt.Errorf("解析微信登录接口响应失败")
	}

	if wxResp.Errcode != 0 {
		l.Errorf("微信接口返回错误: errcode=%d, errmsg=%s", wxResp.Errcode, wxResp.Errmsg)
		return nil, fmt.Errorf("微信登录失败: %s", wxResp.Errmsg)
	}

	if wxResp.Openid == "" {
		return nil, fmt.Errorf("openid为空")
	}

	// 4. 通过 system-rpc 创建或获取基础用户
	createResp, err := l.svcCtx.SystemRpc.CreateUser(l.ctx, &sysPb.CreateUserReq{
		Name:       "",
		OpenId:     wxResp.Openid,
		SessionKey: wxResp.SessionKey,
		ClientId:   req.ClientId,
	})
	if err != nil {
		l.Errorf("创建用户失败: %v", err)
		return nil, fmt.Errorf("登录失败")
	}

	userId := createResp.User.Id

	// 5. 异步初始化各业务服务的用户扩展表
	// 新增小程序：在 switch 里加一个 case 即可
	go func(uid, clientId string) {
		bgCtx := context.Background()
		switch clientId {
		case ClientIdPlant:
			if l.svcCtx.PlantRpc == nil {
				l.Errorf("[MiniLogin] PlantRpc 未配置，跳过 plant profile 初始化")
				return
			}
			if _, err := l.svcCtx.PlantRpc.GetUserProfile(bgCtx, &plantPb.GetProfileReq{
				UserId: uid,
			}); err != nil {
				l.Errorf("[MiniLogin] 初始化 plant profile 失败: userId=%s, err=%v", uid, err)
			} else {
				l.Infof("[MiniLogin] plant profile 初始化成功: userId=%s", uid)
			}
		// case ClientIdRadio:
		//     l.svcCtx.RadioRpc.GetUserProfile(bgCtx, &radioPb.GetProfileReq{UserId: uid})
		default:
			// 未知 clientId 或无需扩展表的场景，跳过
		}
	}(userId, req.ClientId)

	// 6. 生成JWT Token
	token, err := generateToken(l.svcCtx.Config.Auth.AccessSecret, l.svcCtx.Config.Auth.AccessExpire, createResp.User)
	if err != nil {
		l.Errorf("生成Token失败: %v", err)
		return nil, fmt.Errorf("登录失败")
	}

	return &types.LoginResp{
		Token:    token,
		UserInfo: createResp.User,
	}, nil
}
