// ============================================================
// WechatProvider — 微信开放平台 HTTP API 封装
//
// ── 功能 ──────────────────────────────────────────────────
// 实现微信 OAuth 2.0 授权码模式（移动应用），完整流程如下：
//
//   Step 1 (客户端 fluwx):
//     调用 sendWeChatAuth(scope: 'snsapi_userinfo') 拉起微信 App
//     用户点「允许」→ 微信 App 通过 URL Scheme (wx{AppID}://) 回传 code
//     code 一次性，5 分钟有效，只能在服务端使用一次
//
//   Step 2 (本文件 exchangeCodeForToken):
//     GET https://api.weixin.qq.com/sns/oauth2/access_token
//       ?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code
//     成功返回: access_token, refresh_token, openid, scope, unionid
//
//   Step 3 (本文件 getUserInfo):
//     GET https://api.weixin.qq.com/sns/userinfo
//       ?access_token=TOKEN&openid=OPENID&lang=zh_CN
//     返回: nickname, headimgurl, sex, province, city, country, unionid
//
// ── 微信 AppID 申请步骤 ────────────────────────────────────
// 1. 注册微信开放平台账号 (需企业认证，个人无法申请移动应用)
//    https://open.weixin.qq.com → 注册/登录 → 完成开发者资质认证（300元/年）
//
// 2. 创建移动应用
//    管理中心 → 移动应用 → 创建移动应用
//    填写: 应用名称、应用简介、应用图标 (512x512)、应用官网
//    平台信息:
//      Android: 填写应用包名 (cn.gogenex.consumer) + 应用签名 (keystore MD5，32位小写无冒号)
//      iOS:     填写 Bundle ID (cn.gogenex.consumer)
//    提交审核（1-7 个工作日）
//
// 3. 审核通过后获得:
//    AppID (16位，wx 开头) — 公开，可在客户端使用
//    AppSecret (32位)      — 严格保密，只在服务端使用
//
// 4. 接入微信开放平台（启用 unionid）
//    开放平台账号绑定微信公众号 / 小程序 / 移动应用后，
//    同一用户在所有绑定应用中 unionid 相同，可跨 App 识别用户
//
// ── 环境变量配置 ───────────────────────────────────────────
//   WECHAT_APP_ID     — wx 开头的16位 AppID（客户端和服务端都需要）
//   WECHAT_APP_SECRET — 32位应用密钥（严格保密，仅服务端使用）
//
// ── API 特点 ───────────────────────────────────────────────
//   - HTTP 响应始终 200 OK；错误通过响应体 errcode 字段区分
//   - 常见 errcode:
//       40029 = code 无效（已使用或已过期）
//       42003 = code 过期（超过 5 分钟）
//       40163 = code 已被使用
//   - access_token 有效期 7200 秒（2小时），可用 refresh_token 续期
//   - unionid 仅在 snsapi_userinfo scope 且应用已接入开放平台时返回
//
// ── 安全注意事项 ───────────────────────────────────────────
//   - APP_SECRET 只在服务端使用，绝不下发给 App（防止密钥泄露）
//   - code 换 access_token 在服务端完成（防中间人攻击）
//   - state 参数用于防 CSRF：客户端生成随机串，服务端验证（当前简化实现）
//   - 生产环境建议对 code 做防重放：Redis SET NX 标记已用 code，TTL 5 分钟
// ============================================================

import { Injectable, Logger, BadRequestException } from '@nestjs/common';
import * as https from 'https';

/** 微信 access_token 接口响应 */
export interface WechatTokenResponse {
  access_token: string;
  expires_in: number;
  refresh_token: string;
  openid: string;
  scope: string;
  unionid?: string;  // 绑定开放平台账号时才有
  errcode?: number;
  errmsg?: string;
}

/** 微信用户信息接口响应 */
export interface WechatUserInfo {
  openid: string;
  nickname: string;
  sex: number;        // 0=未知 1=男 2=女
  province: string;
  city: string;
  country: string;
  headimgurl: string; // 用户头像 URL，最后一个数值为图片大小（0=640x640）
  privilege: string[];
  unionid?: string;
  errcode?: number;
  errmsg?: string;
}

@Injectable()
export class WechatProvider {
  private readonly logger = new Logger('WechatProvider');
  private readonly appId = process.env.WECHAT_APP_ID;
  private readonly appSecret = process.env.WECHAT_APP_SECRET;

  /**
   * Step 2: 用 code 换取 access_token
   *
   * API: GET https://api.weixin.qq.com/sns/oauth2/access_token
   *   ?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code
   *
   * @param code 客户端从微信 SDK 获取的一次性 code（5 分钟有效）
   */
  async exchangeCodeForToken(code: string): Promise<WechatTokenResponse> {
    const url =
      `https://api.weixin.qq.com/sns/oauth2/access_token` +
      `?appid=${this.appId}&secret=${this.appSecret}` +
      `&code=${code}&grant_type=authorization_code`;

    const data = await this.httpsGet(url);
    const result = JSON.parse(data) as WechatTokenResponse;

    if (result.errcode) {
      this.logger.error(`WeChat token exchange failed: [${result.errcode}] ${result.errmsg}`);
      throw new BadRequestException(`微信授权失败: ${result.errmsg} (code: ${result.errcode})`);
    }

    this.logger.log(`WeChat token exchanged: openid=${result.openid.slice(0, 8)}...`);
    return result;
  }

  /**
   * Step 3: 用 access_token 获取微信用户信息
   *
   * API: GET https://api.weixin.qq.com/sns/userinfo
   *   ?access_token=TOKEN&openid=OPENID&lang=zh_CN
   *
   * 需要 scope = snsapi_userinfo（snsapi_base 只能获取 openid）
   *
   * @param accessToken 来自 exchangeCodeForToken 的 access_token
   * @param openid      来自 exchangeCodeForToken 的 openid
   */
  async getUserInfo(accessToken: string, openid: string): Promise<WechatUserInfo> {
    const url =
      `https://api.weixin.qq.com/sns/userinfo` +
      `?access_token=${accessToken}&openid=${openid}&lang=zh_CN`;

    const data = await this.httpsGet(url);
    const result = JSON.parse(data) as WechatUserInfo;

    if (result.errcode) {
      this.logger.error(`WeChat userinfo failed: [${result.errcode}] ${result.errmsg}`);
      throw new BadRequestException(`获取微信用户信息失败: ${result.errmsg}`);
    }

    return result;
  }

  /** 简单的 HTTPS GET 封装（不引入外部 HTTP 库） */
  private httpsGet(url: string): Promise<string> {
    return new Promise((resolve, reject) => {
      const req = https.get(url, (res) => {
        let body = '';
        res.on('data', (chunk) => (body += chunk));
        res.on('end', () => resolve(body));
      });
      req.on('error', reject);
      req.setTimeout(10000, () => {
        req.destroy();
        reject(new Error('WeChat API timeout'));
      });
    });
  }
}
