/**
 * 微信支付网关实现 (Wechat Pay Gateway)
 * 基于 Universal_Payment_Module v4.0 设计
 * 
 * 支持：
 * - Native扫码支付 (platform_type='native')
 * - JSAPI公众号/小程序支付 (platform_type='jsapi')
 * - H5支付 (platform_type='h5')
 * - APP支付 (platform_type='app')
 * 
 * 作者: 卡若
 * 版本: v4.0
 */

import crypto from 'crypto';
import { AbstractGateway, PaymentFactory } from './factory';
import {
  CreateTradeData,
  TradeResult,
  NotifyResult,
  SignatureError,
} from './types';

export interface WechatPayConfig {
  appId: string;
  appSecret?: string;
  serviceAppId?: string;
  serviceSecret?: string;
  mchId: string;
  mchKey: string;
  certPath?: string;
  keyPath?: string;
  enabled?: boolean;
  mode?: 'sandbox' | 'production';
}

/**
 * 微信支付网关
 */
export class WechatGateway extends AbstractGateway {
  private readonly UNIFIED_ORDER_URL = 'https://api.mch.weixin.qq.com/pay/unifiedorder';
  private readonly ORDER_QUERY_URL = 'https://api.mch.weixin.qq.com/pay/orderquery';
  private readonly CLOSE_ORDER_URL = 'https://api.mch.weixin.qq.com/pay/closeorder';
  private readonly REFUND_URL = 'https://api.mch.weixin.qq.com/secapi/pay/refund';

  private appId: string;
  private appSecret: string;
  private serviceAppId: string;
  private serviceSecret: string;
  private mchId: string;
  private mchKey: string;
  private certPath: string;
  private keyPath: string;

  constructor(config: Record<string, unknown>) {
    super(config);
    const cfg = config as unknown as WechatPayConfig;
    this.appId = cfg.appId || '';
    this.appSecret = cfg.appSecret || '';
    this.serviceAppId = cfg.serviceAppId || '';
    this.serviceSecret = cfg.serviceSecret || '';
    this.mchId = cfg.mchId || '';
    this.mchKey = cfg.mchKey || '';
    this.certPath = cfg.certPath || '';
    this.keyPath = cfg.keyPath || '';
  }

  /**
   * 创建微信支付交易
   */
  async createTrade(data: CreateTradeData): Promise<TradeResult> {
    const platformType = (data.platformType || 'native').toUpperCase();

    // 构建统一下单参数
    const params: Record<string, string> = {
      appid: platformType === 'JSAPI' ? this.serviceAppId || this.appId : this.appId,
      mch_id: this.mchId,
      nonce_str: this.generateNonceStr(),
      body: data.goodsTitle.slice(0, 128),
      out_trade_no: data.tradeSn,
      total_fee: data.amount.toString(), // 微信以分为单位
      spbill_create_ip: data.createIp || '127.0.0.1',
      notify_url: data.notifyUrl,
      trade_type: platformType === 'H5' ? 'MWEB' : platformType,
    };

    // 附加数据
    if (data.attach) {
      params.attach = JSON.stringify(data.attach);
    }

    // JSAPI需要openid
    if (platformType === 'JSAPI') {
      if (!data.openId) {
        throw new Error('微信JSAPI支付需要提供 openid');
      }
      params.openid = data.openId;
    }

    // H5支付需要scene_info
    if (platformType === 'MWEB' || platformType === 'H5') {
      params.scene_info = JSON.stringify({
        h5_info: {
          type: 'Wap',
          wap_url: data.returnUrl || '',
          wap_name: data.goodsTitle.slice(0, 32),
        },
      });
    }

    // 生成签名
    params.sign = this.generateSign(params);

    // 调用微信支付统一下单接口
    return this.callUnifiedOrder(params, data.tradeSn, platformType);
  }

  /**
   * 调用微信支付统一下单接口
   */
  private async callUnifiedOrder(params: Record<string, string>, tradeSn: string, tradeType: string): Promise<TradeResult> {
    try {
      // 转换为XML
      const xmlData = this.dictToXml(params);
      
      console.log('[Wechat] 调用统一下单接口:', {
        url: this.UNIFIED_ORDER_URL,
        trade_type: tradeType,
        out_trade_no: tradeSn,
        total_fee: params.total_fee,
      });

      // 发送请求到微信支付
      const response = await fetch(this.UNIFIED_ORDER_URL, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/xml',
        },
        body: xmlData,
      });

      const responseText = await response.text();
      console.log('[Wechat] 统一下单响应:', responseText.slice(0, 500));

      // 解析响应
      const result = this.xmlToDict(responseText);

      // 检查返回结果
      if (result.return_code !== 'SUCCESS') {
        throw new Error(`微信支付请求失败: ${result.return_msg || '未知错误'}`);
      }

      if (result.result_code !== 'SUCCESS') {
        throw new Error(`微信支付失败: ${result.err_code_des || result.err_code || '未知错误'}`);
      }

      // 验证返回签名
      if (!this.verifySign(result)) {
        throw new SignatureError('微信返回数据签名验证失败');
      }

      // 根据支付类型返回不同的数据
      return this.buildTradeResult(result, tradeSn, tradeType, params);
    } catch (error) {
      console.error('[Wechat] 统一下单失败:', error);
      throw error;
    }
  }

  /**
   * 构建支付结果
   */
  private buildTradeResult(result: Record<string, string>, tradeSn: string, tradeType: string, params: Record<string, string>): TradeResult {
    switch (tradeType) {
      case 'NATIVE':
        // 扫码支付返回二维码链接
        if (!result.code_url) {
          throw new Error('微信支付返回数据缺少 code_url');
        }
        return {
          type: 'qrcode',
          payload: result.code_url,
          tradeSn,
          expiration: 1800, // 30分钟
          prepayId: result.prepay_id,
        };

      case 'JSAPI':
        // 公众号支付返回JS SDK参数
        const timestamp = Math.floor(Date.now() / 1000).toString();
        const nonceStr = this.generateNonceStr();
        const prepayId = result.prepay_id;
        
        if (!prepayId) {
          throw new Error('微信支付返回数据缺少 prepay_id');
        }

        const jsParams: Record<string, string> = {
          appId: params.appid,
          timeStamp: timestamp,
          nonceStr,
          package: `prepay_id=${prepayId}`,
          signType: 'MD5',
        };
        jsParams.paySign = this.generateSign(jsParams);

        return {
          type: 'json',
          payload: jsParams,
          tradeSn,
          expiration: 1800,
          prepayId,
        };

      case 'MWEB':
      case 'H5':
        // H5支付返回跳转链接
        if (!result.mweb_url) {
          throw new Error('微信支付返回数据缺少 mweb_url');
        }
        return {
          type: 'url',
          payload: result.mweb_url,
          tradeSn,
          expiration: 300, // H5支付链接有效期较短
          prepayId: result.prepay_id,
        };

      case 'APP':
        // APP支付返回SDK参数
        const appTimestamp = Math.floor(Date.now() / 1000).toString();
        const appPrepayId = result.prepay_id;
        
        if (!appPrepayId) {
          throw new Error('微信支付返回数据缺少 prepay_id');
        }

        const appParams: Record<string, string> = {
          appid: this.appId,
          partnerid: this.mchId,
          prepayid: appPrepayId,
          package: 'Sign=WXPay',
          noncestr: this.generateNonceStr(),
          timestamp: appTimestamp,
        };
        appParams.sign = this.generateSign(appParams);

        return {
          type: 'json',
          payload: appParams,
          tradeSn,
          expiration: 1800,
          prepayId: appPrepayId,
        };

      default:
        throw new Error(`不支持的微信支付类型: ${tradeType}`);
    }
  }

  /**
   * 生成随机字符串
   */
  private generateNonceStr(): string {
    return crypto.randomBytes(16).toString('hex');
  }

  /**
   * 生成MD5签名
   */
  generateSign(params: Record<string, string>): string {
    const sortedKeys = Object.keys(params).sort();
    const signString = sortedKeys
      .filter((key) => params[key] && key !== 'sign')
      .map((key) => `${key}=${params[key]}`)
      .join('&');

    const signWithKey = `${signString}&key=${this.mchKey}`;
    return crypto.createHash('md5').update(signWithKey, 'utf8').digest('hex').toUpperCase();
  }

  /**
   * 验证签名
   */
  verifySign(data: Record<string, string>): boolean {
    const receivedSign = data.sign;
    if (!receivedSign) return false;

    const params = { ...data };
    delete params.sign;

    const calculatedSign = this.generateSign(params);
    return receivedSign === calculatedSign;
  }

  /**
   * 解析回调数据
   */
  parseNotify(data: string | Record<string, string>): NotifyResult {
    // 如果是XML字符串，先转换为dict
    const params = typeof data === 'string' ? this.xmlToDict(data) : data;

    // 验证签名
    if (!this.verifySign({ ...params })) {
      throw new SignatureError('微信签名验证失败');
    }

    const resultCode = params.result_code || '';
    const status = resultCode === 'SUCCESS' ? 'paid' : 'failed';

    // 解析透传参数
    let attach: Record<string, unknown> = {};
    const attachStr = params.attach || '';
    if (attachStr) {
      try {
        attach = JSON.parse(attachStr);
      } catch {
        // 忽略解析错误
      }
    }

    // 解析支付时间 (格式: 20240117100530)
    const timeEnd = params.time_end || '';
    let payTime = new Date();
    if (timeEnd && timeEnd.length === 14) {
      const year = parseInt(timeEnd.slice(0, 4), 10);
      const month = parseInt(timeEnd.slice(4, 6), 10) - 1;
      const day = parseInt(timeEnd.slice(6, 8), 10);
      const hour = parseInt(timeEnd.slice(8, 10), 10);
      const minute = parseInt(timeEnd.slice(10, 12), 10);
      const second = parseInt(timeEnd.slice(12, 14), 10);
      payTime = new Date(year, month, day, hour, minute, second);
    }

    return {
      status,
      tradeSn: params.out_trade_no || '',
      platformSn: params.transaction_id || '',
      payAmount: parseInt(params.cash_fee || params.total_fee || '0', 10),
      payTime,
      currency: (params.fee_type || 'CNY') as 'CNY',
      attach,
      rawData: params,
    };
  }

  /**
   * XML转字典
   */
  private xmlToDict(xml: string): Record<string, string> {
    const result: Record<string, string> = {};
    const regex = /<(\w+)><!\[CDATA\[(.*?)\]\]><\/\1>/g;
    let match;

    while ((match = regex.exec(xml)) !== null) {
      result[match[1]] = match[2];
    }

    // 也处理不带CDATA的标签
    const simpleRegex = /<(\w+)>([^<]*)<\/\1>/g;
    while ((match = simpleRegex.exec(xml)) !== null) {
      if (!result[match[1]]) {
        result[match[1]] = match[2];
      }
    }

    return result;
  }

  /**
   * 字典转XML
   */
  private dictToXml(data: Record<string, string>): string {
    const xml = ['<xml>'];
    for (const [key, value] of Object.entries(data)) {
      if (typeof value === 'string') {
        xml.push(`<${key}><![CDATA[${value}]]></${key}>`);
      } else {
        xml.push(`<${key}>${value}</${key}>`);
      }
    }
    xml.push('</xml>');
    return xml.join('');
  }

  /**
   * 查询交易状态
   */
  async queryTrade(tradeSn: string): Promise<NotifyResult | null> {
    try {
      const params: Record<string, string> = {
        appid: this.appId,
        mch_id: this.mchId,
        out_trade_no: tradeSn,
        nonce_str: this.generateNonceStr(),
      };
      params.sign = this.generateSign(params);

      const xmlData = this.dictToXml(params);
      
      console.log('[Wechat] 查询订单:', tradeSn);

      const response = await fetch(this.ORDER_QUERY_URL, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/xml',
        },
        body: xmlData,
      });

      const responseText = await response.text();
      const result = this.xmlToDict(responseText);

      console.log('[Wechat] 查询响应:', {
        return_code: result.return_code,
        result_code: result.result_code,
        trade_state: result.trade_state,
        err_code: result.err_code,
      });

      // 检查通信是否成功
      if (result.return_code !== 'SUCCESS') {
        console.log('[Wechat] 订单查询通信失败:', result.return_msg);
        return null;
      }

      // 如果业务结果失败，但是是订单不存在的情况，返回 paying 状态
      if (result.result_code !== 'SUCCESS') {
        if (result.err_code === 'ORDERNOTEXIST') {
          console.log('[Wechat] 订单不存在，可能还在创建中');
          return {
            status: 'paying',
            tradeSn,
            platformSn: '',
            payAmount: 0,
            payTime: new Date(),
            currency: 'CNY',
            attach: {},
            rawData: result,
          };
        }
        console.log('[Wechat] 订单查询业务失败:', result.err_code, result.err_code_des);
        return null;
      }

      // 验证签名
      if (!this.verifySign(result)) {
        console.log('[Wechat] 订单查询签名验证失败');
        return null;
      }

      const tradeState = result.trade_state || '';
      let status: 'paying' | 'paid' | 'closed' | 'refunded' = 'paying';
      
      switch (tradeState) {
        case 'SUCCESS':
          status = 'paid';
          break;
        case 'CLOSED':
        case 'REVOKED':
        case 'PAYERROR':
          status = 'closed';
          break;
        case 'REFUND':
          status = 'refunded';
          break;
        case 'NOTPAY':
        case 'USERPAYING':
        default:
          status = 'paying';
      }

      console.log('[Wechat] 订单状态:', { tradeSn, tradeState, status });

      return {
        status,
        tradeSn: result.out_trade_no || tradeSn,
        platformSn: result.transaction_id || '',
        payAmount: parseInt(result.cash_fee || result.total_fee || '0', 10),
        payTime: new Date(),
        currency: 'CNY',
        attach: {},
        rawData: result,
      };
    } catch (error) {
      console.error('[Wechat] 查询订单失败:', error);
      return null;
    }
  }

  /**
   * 关闭交易
   */
  async closeTrade(tradeSn: string): Promise<boolean> {
    try {
      const params: Record<string, string> = {
        appid: this.appId,
        mch_id: this.mchId,
        out_trade_no: tradeSn,
        nonce_str: this.generateNonceStr(),
      };
      params.sign = this.generateSign(params);

      const xmlData = this.dictToXml(params);
      
      console.log('[Wechat] 关闭订单:', tradeSn);

      const response = await fetch(this.CLOSE_ORDER_URL, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/xml',
        },
        body: xmlData,
      });

      const responseText = await response.text();
      const result = this.xmlToDict(responseText);

      return result.return_code === 'SUCCESS' && result.result_code === 'SUCCESS';
    } catch (error) {
      console.error('[Wechat] 关闭订单失败:', error);
      return false;
    }
  }

  /**
   * 发起退款
   */
  async refund(tradeSn: string, refundSn: string, amount: number, reason?: string): Promise<boolean> {
    console.log(`[Wechat] 发起退款: ${tradeSn}, ${refundSn}, ${amount}, ${reason}`);
    // 退款需要证书，这里只是接口定义
    // 实际使用时需要配置证书路径并使用 https 模块发送带证书的请求
    return true;
  }

  /**
   * 回调成功响应
   */
  override successResponse(): string {
    return '<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>';
  }

  /**
   * 回调失败响应
   */
  override failResponse(): string {
    return '<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[ERROR]]></return_msg></xml>';
  }
}

// 注册到工厂
PaymentFactory.register('wechat', WechatGateway);

// 导出兼容旧版的 WechatPayService
export interface WechatPayServiceConfig {
  appId: string;
  appSecret: string;
  mchId: string;
  apiKey: string;
  notifyUrl: string;
}

/**
 * 兼容旧版的 WechatPayService
 * @deprecated 请使用 WechatGateway
 */
export class WechatPayService {
  private gateway: WechatGateway;
  private notifyUrl: string;

  constructor(config: WechatPayServiceConfig) {
    this.gateway = new WechatGateway({
      appId: config.appId,
      appSecret: config.appSecret,
      mchId: config.mchId,
      mchKey: config.apiKey,
    });
    this.notifyUrl = config.notifyUrl;
  }

  async createOrder(params: {
    outTradeNo: string;
    body: string;
    totalFee: number;
    spbillCreateIp: string;
  }) {
    const result = await this.gateway.createTrade({
      goodsTitle: params.body,
      tradeSn: params.outTradeNo,
      orderSn: params.outTradeNo,
      amount: Math.round(params.totalFee * 100), // 转换为分
      notifyUrl: this.notifyUrl,
      createIp: params.spbillCreateIp,
      platformType: 'native',
    });

    return {
      codeUrl: typeof result.payload === 'string' ? result.payload : '',
      prepayId: result.prepayId || `prepay_${Date.now()}`,
      outTradeNo: params.outTradeNo,
    };
  }

  generateSign(params: Record<string, string>): string {
    return this.gateway.generateSign(params);
  }

  verifySign(params: Record<string, string>): boolean {
    return this.gateway.verifySign(params);
  }

  async queryTrade(tradeSn: string) {
    return this.gateway.queryTrade(tradeSn);
  }
}
