/**
 * 支付宝网关实现 (Alipay Gateway)
 * 基于 Universal_Payment_Module v4.0 设计
 * 
 * 支持：
 * - 电脑网站支付 (platform_type='web')
 * - 手机网站支付 (platform_type='wap')
 * - 扫码支付 (platform_type='qr')
 * 
 * 作者: 卡若
 * 版本: v4.0
 */

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

export interface AlipayConfig {
  appId: string;
  pid: string;
  sellerEmail?: string;
  privateKey?: string;
  publicKey?: string;
  md5Key?: string;
  enabled?: boolean;
  mode?: 'sandbox' | 'production';
}

/**
 * 支付宝网关
 */
export class AlipayGateway extends AbstractGateway {
  private readonly GATEWAY_URL = 'https://openapi.alipay.com/gateway.do';
  private readonly SANDBOX_URL = 'https://openapi.alipaydev.com/gateway.do';

  private appId: string;
  private pid: string;
  private sellerEmail: string;
  private privateKey: string;
  private publicKey: string;
  private md5Key: string;
  private mode: 'sandbox' | 'production';

  constructor(config: Record<string, unknown>) {
    super(config);
    const cfg = config as unknown as AlipayConfig;
    this.appId = cfg.appId || '';
    this.pid = cfg.pid || '';
    this.sellerEmail = cfg.sellerEmail || '';
    this.privateKey = cfg.privateKey || '';
    this.publicKey = cfg.publicKey || '';
    this.md5Key = cfg.md5Key || '';
    this.mode = cfg.mode || 'production';
  }

  /**
   * 获取网关地址
   */
  private getGatewayUrl(): string {
    return this.mode === 'sandbox' ? this.SANDBOX_URL : this.GATEWAY_URL;
  }

  /**
   * 创建支付宝交易
   */
  async createTrade(data: CreateTradeData): Promise<TradeResult> {
    const platformType = (data.platformType || 'wap').toLowerCase();

    switch (platformType) {
      case 'web':
        return this.createWebTrade(data);
      case 'wap':
        return this.createWapTrade(data);
      case 'qr':
        return this.createQrTrade(data);
      default:
        // 默认使用 WAP 支付
        return this.createWapTrade(data);
    }
  }

  /**
   * 电脑网站支付
   */
  private async createWebTrade(data: CreateTradeData): Promise<TradeResult> {
    const bizContent = {
      subject: data.goodsTitle.slice(0, 256),
      out_trade_no: data.tradeSn,
      total_amount: fenToYuan(data.amount).toFixed(2),
      product_code: 'FAST_INSTANT_TRADE_PAY',
      body: data.goodsDetail?.slice(0, 128) || '',
      passback_params: data.attach ? encodeURIComponent(JSON.stringify(data.attach)) : '',
    };

    const params = this.buildParams('alipay.trade.page.pay', bizContent, data.returnUrl, data.notifyUrl);
    const sign = this.generateMD5Sign(params);
    params.sign = sign;

    const payUrl = `${this.getGatewayUrl()}?${this.buildQueryString(params)}`;

    console.log('[Alipay] 创建电脑网站支付:', {
      out_trade_no: data.tradeSn,
      total_amount: fenToYuan(data.amount).toFixed(2),
    });

    return {
      type: 'url',
      payload: payUrl,
      tradeSn: data.tradeSn,
      expiration: 1800,
    };
  }

  /**
   * 手机网站支付
   */
  private async createWapTrade(data: CreateTradeData): Promise<TradeResult> {
    const bizContent = {
      subject: data.goodsTitle.slice(0, 256),
      out_trade_no: data.tradeSn,
      total_amount: fenToYuan(data.amount).toFixed(2),
      product_code: 'QUICK_WAP_WAY',
      body: data.goodsDetail?.slice(0, 128) || '',
      passback_params: data.attach ? encodeURIComponent(JSON.stringify(data.attach)) : '',
    };

    const params = this.buildParams('alipay.trade.wap.pay', bizContent, data.returnUrl, data.notifyUrl);
    const sign = this.generateMD5Sign(params);
    params.sign = sign;

    const payUrl = `${this.getGatewayUrl()}?${this.buildQueryString(params)}`;

    console.log('[Alipay] 创建手机网站支付:', {
      out_trade_no: data.tradeSn,
      total_amount: fenToYuan(data.amount).toFixed(2),
    });

    return {
      type: 'url',
      payload: payUrl,
      tradeSn: data.tradeSn,
      expiration: 1800,
    };
  }

  /**
   * 扫码支付（当面付）
   */
  private async createQrTrade(data: CreateTradeData): Promise<TradeResult> {
    const bizContent = {
      subject: data.goodsTitle.slice(0, 256),
      out_trade_no: data.tradeSn,
      total_amount: fenToYuan(data.amount).toFixed(2),
      body: data.goodsDetail?.slice(0, 128) || '',
    };

    const params = this.buildParams('alipay.trade.precreate', bizContent, '', data.notifyUrl);
    const sign = this.generateMD5Sign(params);
    params.sign = sign;

    console.log('[Alipay] 创建扫码支付:', {
      out_trade_no: data.tradeSn,
      total_amount: fenToYuan(data.amount).toFixed(2),
    });

    try {
      // 调用支付宝预下单接口
      const response = await fetch(`${this.getGatewayUrl()}?${this.buildQueryString(params)}`, {
        method: 'GET',
      });

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

      // 解析JSON响应
      const result = JSON.parse(responseText);
      const precreateResponse = result.alipay_trade_precreate_response;

      if (precreateResponse && precreateResponse.code === '10000' && precreateResponse.qr_code) {
        return {
          type: 'qrcode',
          payload: precreateResponse.qr_code,
          tradeSn: data.tradeSn,
          expiration: 1800,
        };
      }

      // 如果API调用失败，回退到WAP支付方式
      console.log('[Alipay] 预下单失败，使用WAP支付:', precreateResponse?.sub_msg || precreateResponse?.msg);
      return this.createWapTrade(data);
    } catch (error) {
      console.error('[Alipay] 预下单异常，使用WAP支付:', error);
      return this.createWapTrade(data);
    }
  }

  /**
   * 构建公共参数
   */
  private buildParams(
    method: string,
    bizContent: Record<string, string>,
    returnUrl?: string,
    notifyUrl?: string
  ): Record<string, string> {
    const timestamp = new Date().toISOString().slice(0, 19).replace('T', ' ');

    const params: Record<string, string> = {
      app_id: this.appId,
      method,
      charset: 'utf-8',
      sign_type: 'MD5',
      timestamp,
      version: '1.0',
      biz_content: JSON.stringify(bizContent),
    };

    if (returnUrl) {
      params.return_url = returnUrl;
    }
    if (notifyUrl) {
      params.notify_url = notifyUrl;
    }

    return params;
  }

  /**
   * 生成MD5签名
   */
  private generateMD5Sign(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}${this.md5Key}`;
    return crypto.createHash('md5').update(signWithKey, 'utf8').digest('hex');
  }

  /**
   * 构建查询字符串
   */
  private buildQueryString(params: Record<string, string>): string {
    return Object.entries(params)
      .map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`)
      .join('&');
  }

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

    // 复制数据，移除 sign 和 sign_type
    const params = { ...data };
    delete params.sign;
    delete params.sign_type;

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

  /**
   * 解析回调数据
   */
  parseNotify(data: string | Record<string, string>): NotifyResult {
    const params = typeof data === 'string' ? this.parseFormData(data) : data;

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

    const tradeStatus = params.trade_status || '';
    const status = ['TRADE_SUCCESS', 'TRADE_FINISHED'].includes(tradeStatus) ? 'paid' : 'failed';

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

    // 解析支付时间
    const gmtPayment = params.gmt_payment || '';
    const payTime = gmtPayment ? new Date(gmtPayment) : new Date();

    return {
      status,
      tradeSn: params.out_trade_no || '',
      platformSn: params.trade_no || '',
      payAmount: yuanToFen(parseFloat(params.total_amount || '0')),
      payTime,
      currency: 'CNY',
      attach,
      rawData: params,
    };
  }

  /**
   * 解析表单数据
   */
  private parseFormData(formString: string): Record<string, string> {
    const result: Record<string, string> = {};
    const pairs = formString.split('&');
    for (const pair of pairs) {
      const [key, value] = pair.split('=');
      if (key && value !== undefined) {
        result[decodeURIComponent(key)] = decodeURIComponent(value);
      }
    }
    return result;
  }

  /**
   * 查询交易状态
   */
  async queryTrade(tradeSn: string): Promise<NotifyResult | null> {
    try {
      // 检查 appId 是否配置
      if (!this.appId) {
        console.log('[Alipay] 查询跳过: 未配置 appId');
        return {
          status: 'paying',
          tradeSn,
          platformSn: '',
          payAmount: 0,
          payTime: new Date(),
          currency: 'CNY',
          attach: {},
          rawData: {},
        };
      }

      const bizContent = {
        out_trade_no: tradeSn,
      };

      const params = this.buildParams('alipay.trade.query', bizContent);
      params.sign = this.generateMD5Sign(params);

      console.log('[Alipay] 查询订单:', { tradeSn, appId: this.appId });

      const response = await fetch(`${this.getGatewayUrl()}?${this.buildQueryString(params)}`, {
        method: 'GET',
      });

      const responseText = await response.text();
      console.log('[Alipay] 查询响应:', responseText.slice(0, 300));
      
      const result = JSON.parse(responseText);
      const queryResponse = result.alipay_trade_query_response;

      // 如果订单不存在，返回 paying 状态（可能还没同步到支付宝）
      if (queryResponse?.code === '40004' && queryResponse?.sub_code === 'ACQ.TRADE_NOT_EXIST') {
        console.log('[Alipay] 订单不存在，可能还在等待支付');
        return {
          status: 'paying',
          tradeSn,
          platformSn: '',
          payAmount: 0,
          payTime: new Date(),
          currency: 'CNY',
          attach: {},
          rawData: queryResponse,
        };
      }

      if (!queryResponse || queryResponse.code !== '10000') {
        console.log('[Alipay] 订单查询失败:', {
          code: queryResponse?.code,
          msg: queryResponse?.msg,
          sub_code: queryResponse?.sub_code,
          sub_msg: queryResponse?.sub_msg,
        });
        // 返回 paying 状态而不是 null，让前端继续轮询
        return {
          status: 'paying',
          tradeSn,
          platformSn: '',
          payAmount: 0,
          payTime: new Date(),
          currency: 'CNY',
          attach: {},
          rawData: queryResponse || {},
        };
      }

      const tradeStatus = queryResponse.trade_status || '';
      let status: 'paying' | 'paid' | 'closed' | 'refunded' = 'paying';
      
      switch (tradeStatus) {
        case 'TRADE_SUCCESS':
        case 'TRADE_FINISHED':
          status = 'paid';
          break;
        case 'TRADE_CLOSED':
          status = 'closed';
          break;
        case 'WAIT_BUYER_PAY':
        default:
          status = 'paying';
      }

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

      return {
        status,
        tradeSn: queryResponse.out_trade_no || tradeSn,
        platformSn: queryResponse.trade_no || '',
        payAmount: yuanToFen(parseFloat(queryResponse.total_amount || '0')),
        payTime: new Date(queryResponse.send_pay_date || Date.now()),
        currency: 'CNY',
        attach: {},
        rawData: queryResponse,
      };
    } catch (error) {
      console.error('[Alipay] 查询订单失败:', error);
      // 返回 paying 状态而不是 null
      return {
        status: 'paying',
        tradeSn,
        platformSn: '',
        payAmount: 0,
        payTime: new Date(),
        currency: 'CNY',
        attach: {},
        rawData: {},
      };
    }
  }

  /**
   * 关闭交易
   */
  async closeTrade(tradeSn: string): Promise<boolean> {
    try {
      const bizContent = {
        out_trade_no: tradeSn,
      };

      const params = this.buildParams('alipay.trade.close', bizContent);
      params.sign = this.generateMD5Sign(params);

      console.log('[Alipay] 关闭订单:', tradeSn);

      const response = await fetch(`${this.getGatewayUrl()}?${this.buildQueryString(params)}`, {
        method: 'GET',
      });

      const responseText = await response.text();
      const result = JSON.parse(responseText);
      const closeResponse = result.alipay_trade_close_response;

      return closeResponse && closeResponse.code === '10000';
    } catch (error) {
      console.error('[Alipay] 关闭订单失败:', error);
      return false;
    }
  }

  /**
   * 发起退款
   */
  async refund(tradeSn: string, refundSn: string, amount: number, reason?: string): Promise<boolean> {
    try {
      const bizContent = {
        out_trade_no: tradeSn,
        out_request_no: refundSn,
        refund_amount: fenToYuan(amount).toFixed(2),
        refund_reason: reason || '用户退款',
      };

      const params = this.buildParams('alipay.trade.refund', bizContent);
      params.sign = this.generateMD5Sign(params);

      console.log('[Alipay] 发起退款:', { tradeSn, refundSn, amount });

      const response = await fetch(`${this.getGatewayUrl()}?${this.buildQueryString(params)}`, {
        method: 'GET',
      });

      const responseText = await response.text();
      const result = JSON.parse(responseText);
      const refundResponse = result.alipay_trade_refund_response;

      return refundResponse && refundResponse.code === '10000';
    } catch (error) {
      console.error('[Alipay] 退款失败:', error);
      return false;
    }
  }

  /**
   * 回调成功响应
   */
  override successResponse(): string {
    return 'success';
  }

  /**
   * 回调失败响应
   */
  override failResponse(): string {
    return 'fail';
  }
}

// 注册到工厂
PaymentFactory.register('alipay', AlipayGateway);

// 导出兼容旧版的 AlipayService
export interface AlipayServiceConfig {
  appId: string;
  partnerId: string;
  key: string;
  returnUrl: string;
  notifyUrl: string;
}

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

  constructor(config: AlipayServiceConfig) {
    this.gateway = new AlipayGateway({
      appId: config.appId,
      pid: config.partnerId,
      md5Key: config.key,
    });
    this.notifyUrl = config.notifyUrl;
    this.returnUrl = config.returnUrl;
  }

  createOrder(params: {
    outTradeNo: string;
    subject: string;
    totalAmount: number;
    body?: string;
  }) {
    // 同步创建订单信息
    const orderInfo: Record<string, string> = {
      app_id: (this.gateway as AlipayGateway)['appId'],
      method: 'alipay.trade.wap.pay',
      format: 'JSON',
      charset: 'utf-8',
      sign_type: 'MD5',
      timestamp: new Date().toISOString().slice(0, 19).replace('T', ' '),
      version: '1.0',
      notify_url: this.notifyUrl,
      return_url: this.returnUrl,
      biz_content: JSON.stringify({
        out_trade_no: params.outTradeNo,
        product_code: 'QUICK_WAP_WAY',
        total_amount: params.totalAmount.toFixed(2),
        subject: params.subject,
        body: params.body || params.subject,
      }),
    };

    const sign = this.generateSign(orderInfo);
    return {
      ...orderInfo,
      sign,
      paymentUrl: this.buildPaymentUrl(orderInfo, sign),
    };
  }

  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 md5Key = (this.gateway as AlipayGateway)['md5Key'];
    const signWithKey = `${signString}${md5Key}`;
    return crypto.createHash('md5').update(signWithKey, 'utf8').digest('hex');
  }

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

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

  private buildPaymentUrl(params: Record<string, string>, sign: string): string {
    const gateway = 'https://openapi.alipay.com/gateway.do';
    const queryParams = new URLSearchParams({ ...params, sign });
    return `${gateway}?${queryParams.toString()}`;
  }
}
