const dayjs = require('dayjs');
const axios = require('axios');

module.exports = class extends think.Service {
  /**
   * 刷新单个应用的 access_token
   * @param {Object} app 应用配置
   * @returns {Object} 刷新结果
   */
  async refreshToken(app) {
    const now = dayjs().unix();
    const diffSeconds = app.token_expires_at ? app.token_expires_at - now : 0;

    // 大于10分钟(600秒)跳过
    if (diffSeconds > 600) {
      return {
        success: true,
        app_name: app.app_name,
        skipped: true,
        expires_in: Math.floor(diffSeconds / 60) + '分钟'
      };
    }

    // 调用稳定版接口
    const url = 'https://api.weixin.qq.com/cgi-bin/stable_token';
    try {
      const { data: res } = await axios.post(url, {
        grant_type: 'client_credential',
        appid: app.app_id,
        secret: app.app_secret,
        force_refresh: diffSeconds <= 600 && diffSeconds > 0
      });

      if (res.access_token) {
        const tokenExpiresAt = dayjs().unix() + res.expires_in;
        await this.model('sys_wechat_app').where({ id: app.id }).update({
          access_token: res.access_token,
          token_expires_at: tokenExpiresAt
        });
        return {
          success: true,
          app_name: app.app_name,
          refreshed: true,
          expires_at: dayjs.unix(tokenExpiresAt).format('YYYY-MM-DD HH:mm:ss')
        };
      }
      return { success: false, app_name: app.app_name, error: res.errmsg || '未知错误' };
    } catch (error) {
      think.logger.error(`刷新token失败[${app.app_name}]:`, error);
      return { success: false, app_name: app.app_name, error: error.message };
    }
  }

  /**
   * 刷新所有启用应用的 token
   * @returns {Array} 刷新结果列表
   */
  async refreshAllTokens() {
    const apps = await this.model('sys_wechat_app').where({ status: 1 }).select();
    const results = [];
    for (const app of apps) {
      const result = await this.refreshToken(app);
      results.push(result);
    }
    return results;
  }

  /**
   * 检查并刷新过期的 token
   * @param {Object} app 应用配置
   * @returns {String|null} access_token
   */
  async checkAndRefreshToken(app) {
    if (think.isEmpty(app)) return null;

    const now = dayjs().unix();
    // token 不存在或已过期（小于1分钟视为过期）
    if (!app.access_token || !app.token_expires_at || app.token_expires_at - now < 60) {
      const result = await this.refreshToken(app);
      if (result.success && result.refreshed) {
        // 重新查询获取最新 token
        const updated = await this.model('sys_wechat_app').where({ id: app.id }).find();
        return updated.access_token;
      }
      return null;
    }
    return app.access_token;
  }

  /**
   * 获取 access_token（供其他模块调用）
   * @param {String} appId 应用AppID
   * @returns {String|null} access_token
   */
  async getAccessToken(appId) {
    const app = await this.model('sys_wechat_app').where({ app_id: appId, status: 1 }).find();
    return this.checkAndRefreshToken(app);
  }

  /**
   * 根据 remark 获取 access_token
   * @param {String} remark 备注标识，如 pap_mini
   * @returns {String|null} access_token
   */
  async getAccessTokenByRemark(remark) {
    const app = await this.model('sys_wechat_app').where({ remark, status: 1 }).find();
    return this.checkAndRefreshToken(app);
  }

  /**
   * 用 code 换取 openid 和 session_key（通用方法）
   * @param {String} code 微信登录 code
   * @param {String} remark 应用标识，如 pap_mini、pap_mini_doctor
   * @returns {Object} { openid, session_key, unionid }
   */
  async code2Session(code, remark) {
    const app = await this.model('sys_wechat_app').where({ remark, status: 1 }).find();
    if (think.isEmpty(app)) {
      throw new Error(`未找到应用配置: ${remark}`);
    }

    const url = `https://api.weixin.qq.com/sns/jscode2session?appid=${app.app_id}&secret=${app.app_secret}&js_code=${code}&grant_type=authorization_code`;

    const { data } = await axios.get(url, { timeout: 10000 });

    if (data.errcode) {
      think.logger.error('code2Session失败:', data);
      throw new Error(data.errmsg || '获取openid失败');
    }

    return {
      openid: data.openid,
      session_key: data.session_key,
      unionid: data.unionid || null
    };
  }

  /**
   * 获取应用的订阅消息模板配置
   * @param {String} remark 应用标识
   * @returns {Object} 模板配置 { audit_result: 'xxx', ... }
   */
  async getSubscribeTemplates(remark) {
    const app = await this.model('sys_wechat_app').where({ remark, status: 1 }).find();
    if (think.isEmpty(app) || !app.subscribe_templates) return {};
    try {
      return JSON.parse(app.subscribe_templates);
    } catch (e) {
      return {};
    }
  }

  /**
   * 发送订阅消息
   * @param {Object} options
   * @param {String} options.remark 应用标识
   * @param {String} options.openid 接收者openid
   * @param {String} options.templateId 模板ID
   * @param {String} options.page 跳转页面
   * @param {Object} options.data 模板数据
   * @param {String} options.miniprogramState 小程序版本: developer/trial/formal，默认formal
   * @returns {Object} { success, error }
   */
  async sendSubscribeMessage({ remark, openid, templateId, page, data, miniprogramState }) {
    try {
      const accessToken = await this.getAccessTokenByRemark(remark);
      if (!accessToken) {
        think.logger.error('[Subscribe] 获取access_token失败');
        return { success: false, error: '获取access_token失败' };
      }
      const url = `https://api.weixin.qq.com/cgi-bin/message/subscribe/send?access_token=${accessToken}`;
      const body = {
        touser: openid,
        template_id: templateId,
        page: page || '',
        data,
        miniprogram_state: miniprogramState || 'formal'
      };
      const { data: res } = await axios.post(url, body, { timeout: 10000 });
      if (res.errcode === 0) {
        think.logger.info(`[Subscribe] 发送成功 openid=${openid} tmpl=${templateId}`);
        return { success: true };
      }
      think.logger.error(`[Subscribe] 发送失败:`, res);
      return { success: false, error: res.errmsg || '发送失败' };
    } catch (error) {
      think.logger.error('[Subscribe] 发送异常:', error);
      return { success: false, error: error.message };
    }
  }

  /**
   * 用 phoneCode 换取用户手机号
   * @param {String} phoneCode - 前端 getPhoneNumber 回调中的 code
   * @param {String} remark - 应用标识，如 pap_mini
   * @returns {Object} { phoneNumber, purePhoneNumber, countryCode }
   */
  async getPhoneNumber(phoneCode, remark) {
    const accessToken = await this.getAccessTokenByRemark(remark);
    if (!accessToken) {
      throw new Error('获取access_token失败');
    }
    const url = `https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token=${accessToken}`;
    const { data } = await axios.post(url, { code: phoneCode }, { timeout: 10000 });
    if (data.errcode) {
      think.logger.error('getPhoneNumber失败:', data);
      throw new Error(data.errmsg || '获取手机号失败');
    }
    return data.phone_info;
  }

};
