import { Injectable, Logger } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import * as crypto from 'crypto';
import {
  PaymentProviderPort,
  PaymentProviderType,
  CreatePaymentParams,
  PaymentSession,
  PaymentResult,
  WebhookResult,
} from '../../../domain/ports/payment-provider.port';
import { InvoiceCurrency } from '../../../domain/entities/invoice.entity';

@Injectable()
export class WeChatPayProvider implements PaymentProviderPort {
  readonly providerType = PaymentProviderType.WECHAT_PAY;
  private readonly logger = new Logger(WeChatPayProvider.name);
  private mchId: string;
  private apiKeyV3: string;
  private appId: string;
  private configured = false;

  // WeChat Pay v3 API base
  private static readonly BASE_URL = 'https://api.mch.weixin.qq.com';

  constructor(private readonly configService: ConfigService) {
    this.mchId = this.configService.get<string>('WECHAT_MCH_ID', '');
    this.apiKeyV3 = this.configService.get<string>('WECHAT_API_KEY_V3', '');
    this.appId = this.configService.get<string>('WECHAT_APP_ID', '');
    this.configured = !!(this.mchId && this.apiKeyV3 && this.appId);
  }

  isConfigured(): boolean {
    return this.configured;
  }

  async createPaymentSession(params: CreatePaymentParams): Promise<PaymentSession> {
    if (!this.configured) throw new Error('WeChat Pay not configured');

    if (params.currency !== InvoiceCurrency.CNY) {
      throw new Error('WeChat Pay only supports CNY payments');
    }

    const outTradeNo = `it0-${params.invoiceId}-${Date.now()}`;
    const notifyUrl = this.configService.get<string>(
      'WECHAT_NOTIFY_URL',
      'https://it0api.szaiai.com/api/v1/billing/webhooks/wechat',
    );

    const body = {
      appid: this.appId,
      mchid: this.mchId,
      description: params.description,
      out_trade_no: outTradeNo,
      notify_url: notifyUrl,
      amount: { total: params.amount, currency: 'CNY' },
    };

    const response = await this.request('POST', '/v3/pay/transactions/native', body);
    const qrCodeUrl = response.code_url;

    return {
      providerPaymentId: outTradeNo,
      qrCodeUrl,
      expiresAt: new Date(Date.now() + 2 * 60 * 60 * 1000),
    };
  }

  async confirmPayment(providerPaymentId: string): Promise<PaymentResult> {
    // WeChat Pay uses async webhook notification; sync confirm not needed
    return { providerPaymentId, status: 'pending', amount: 0, currency: 'CNY' };
  }

  async handleWebhook(rawBody: Buffer, headers: Record<string, string>): Promise<WebhookResult> {
    if (!this.configured) throw new Error('WeChat Pay not configured');

    // Verify HMAC-SHA256 signature
    const timestamp = headers['wechatpay-timestamp'];
    const nonce = headers['wechatpay-nonce'];
    const signature = headers['wechatpay-signature'];
    const message = `${timestamp}\n${nonce}\n${rawBody.toString()}\n`;

    const expectedSig = crypto
      .createHmac('sha256', this.apiKeyV3)
      .update(message)
      .digest('base64');

    if (signature !== expectedSig) {
      throw new Error('WeChat Pay webhook signature invalid');
    }

    const payload = JSON.parse(rawBody.toString());
    const eventType = payload.event_type;

    if (eventType === 'TRANSACTION.SUCCESS') {
      // Decrypt resource
      const resource = payload.resource;
      const decrypted = this.decryptResource(resource.ciphertext, resource.associated_data, resource.nonce);
      const transaction = JSON.parse(decrypted);
      return { type: 'payment_succeeded', providerPaymentId: transaction.out_trade_no };
    } else if (eventType === 'TRANSACTION.FAIL') {
      const resource = payload.resource;
      const decrypted = this.decryptResource(resource.ciphertext, resource.associated_data, resource.nonce);
      const transaction = JSON.parse(decrypted);
      return { type: 'payment_failed', providerPaymentId: transaction.out_trade_no };
    }

    return { type: 'unknown' };
  }

  private decryptResource(ciphertext: string, associatedData: string, nonce: string): string {
    const key = Buffer.from(this.apiKeyV3, 'utf-8');
    const ciphertextBuffer = Buffer.from(ciphertext, 'base64');
    const authTag = ciphertextBuffer.slice(-16);
    const encrypted = ciphertextBuffer.slice(0, -16);

    const decipher = crypto.createDecipheriv('aes-256-gcm', key, nonce);
    decipher.setAuthTag(authTag);
    decipher.setAAD(Buffer.from(associatedData));

    return Buffer.concat([decipher.update(encrypted), decipher.final()]).toString('utf-8');
  }

  private async request(method: string, path: string, body: any): Promise<any> {
    const url = `${WeChatPayProvider.BASE_URL}${path}`;
    const timestamp = Math.floor(Date.now() / 1000);
    const nonce = crypto.randomBytes(16).toString('hex');
    const bodyStr = JSON.stringify(body);
    const message = `${method}\n${path}\n${timestamp}\n${nonce}\n${bodyStr}\n`;

    // HMAC-SHA256 signature for request
    const signature = crypto
      .createHmac('sha256', this.apiKeyV3)
      .update(message)
      .digest('base64');

    const authorization =
      `WECHATPAY2-SHA256-RSA2048 mchid="${this.mchId}",` +
      `nonce_str="${nonce}",timestamp="${timestamp}",` +
      `signature="${signature}"`;

    const res = await fetch(url, {
      method,
      headers: {
        'Content-Type': 'application/json',
        Authorization: authorization,
        Accept: 'application/json',
      },
      body: bodyStr,
    });

    if (!res.ok) {
      const text = await res.text();
      throw new Error(`WeChat Pay API error ${res.status}: ${text}`);
    }

    return res.json();
  }
}
