import { Injectable, Logger } from '@nestjs/common';
import { PrismaService } from '../../../infrastructure/database/prisma.service';
import { AppStoreJwsVerifier } from './app-store-jws-verifier';
import * as crypto from 'crypto';

/**
 * M-MEMBER-01-06: App Store Server Notifications V2 处理服务
 *
 * Durable Inbox：received → processing → processed/failed/ignored
 * 生命周期投影：根据通知类型更新 Apple Grant
 */

interface NotificationPayload {
  notificationUUID: string;
  notificationType: string;
  subtype?: string;
  data?: {
    appAccountToken?: string;
    bundleId?: string;
    environment?: string;
    signedDate?: number;
    signedTransactionInfo?: string;
  };
}

const SUPPORTED_TYPES = new Set([
  'SUBSCRIBED', 'DID_RENEW', 'DID_CHANGE_RENEWAL_STATUS',
  'DID_FAIL_TO_RENEW', 'DID_RECOVER', 'GRACE_PERIOD_EXPIRED',
  'EXPIRED', 'REFUND', 'REVOKE',
]);

@Injectable()
export class AppStoreNotificationService {
  private readonly logger = new Logger(AppStoreNotificationService.name);

  constructor(
    private readonly prisma: PrismaService,
    private readonly verifier: AppStoreJwsVerifier,
  ) {}

  /**
   * 异步处理通知。由 Controller 在 200 响应后调用。
   */
  async processNotification(signedPayload: string): Promise<void> {
    // 1. 验证 JWS
    let payload: NotificationPayload;
    try {
      const verified = await this.verifier.verify(signedPayload);
      payload = verified as unknown as NotificationPayload;
    } catch (err: any) {
      this.logger.error(`Notification JWS verification failed: ${err.message}`);
      return;
    }

    const uuid = payload.notificationUUID;
    const hash = crypto.createHash('sha256').update(signedPayload).digest('hex');

    // 2. Durable Inbox: 幂等落库
    const inbox = await this.upsertInbox(uuid, payload, hash);
    if (inbox.processedStatus !== 'received') {
      this.logger.log(`Duplicate notification ${uuid}, already ${inbox.processedStatus}`);
      return;
    }

    // 3. CAS: received → processing
    const claimed = await this.claimForProcessing(inbox.id);
    if (!claimed) {
      this.logger.warn(`Notification ${uuid} already claimed by another worker`);
      return;
    }

    // 4. 处理
    try {
      if (!SUPPORTED_TYPES.has(payload.notificationType)) {
        await this.markIgnored(inbox.id, payload.notificationType);
        return;
      }
      await this.applyLifecycleEvent(payload);
      await this.markProcessed(inbox.id);
    } catch (err: any) {
      this.logger.error(`Notification ${uuid} processing failed: ${err.message}`);
      await this.markFailed(inbox.id, err.message);
    }
  }

  // ── Durable Inbox ──

  private async upsertInbox(uuid: string, payload: NotificationPayload, hash: string) {
    const existing = await this.prisma.appStoreNotification.findUnique({
      where: { notificationUuid: uuid },
    });
    if (existing) return existing;

    return this.prisma.appStoreNotification.create({
      data: {
        notificationUuid: uuid,
        notificationType: payload.notificationType,
        subtype: payload.subtype ?? null,
        environment: payload.data?.environment ?? 'Unknown',
        signedDate: payload.data?.signedDate ? new Date(payload.data.signedDate) : new Date(),
        payloadHash: hash,
        processedStatus: 'received',
      },
    });
  }

  private async claimForProcessing(id: string): Promise<boolean> {
    // CAS: received → processing
    const result = await this.prisma.appStoreNotification.updateMany({
      where: { id, processedStatus: 'received' },
      data: {
        processedStatus: 'processing',
        processingStartedAt: new Date(),
      },
    });
    return result.count > 0;
  }

  private async markProcessed(id: string) {
    await this.prisma.appStoreNotification.update({
      where: { id },
      data: { processedStatus: 'processed', processedAt: new Date() },
    });
  }

  private async markFailed(id: string, error: string) {
    const existing = await this.prisma.appStoreNotification.findUnique({ where: { id } });
    const retryCount = (existing?.retryCount ?? 0) + 1;
    const maxRetries = 5;
    await this.prisma.appStoreNotification.update({
      where: { id },
      data: {
        processedStatus: retryCount >= maxRetries ? 'failed' : 'received',
        errorMessage: error?.substring(0, 1000),
        retryCount,
        nextRetryAt: retryCount < maxRetries ? new Date(Date.now() + Math.pow(2, retryCount) * 10000) : null,
      },
    });
  }

  private async markIgnored(id: string, type: string) {
    this.logger.log(`Ignoring unknown notification type: ${type}`);
    await this.prisma.appStoreNotification.update({
      where: { id },
      data: { processedStatus: 'ignored', processedAt: new Date() },
    });
  }

  // ── Lifecycle Projection ──

  private async applyLifecycleEvent(payload: NotificationPayload) {
    const type = payload.notificationType;
    const env = payload.data?.environment ?? 'Unknown';

    // 解析内层 JWS 提取关键字段
    let txId: string | null = null;
    let originalTxId: string | null = null;
    let expiresDate: Date | null = null;
    if (payload.data?.signedTransactionInfo) {
      try {
        const txPayload = await this.verifier.verify(payload.data.signedTransactionInfo);
        const tx = txPayload as any;
        txId = tx.transactionId ?? null;
        originalTxId = tx.originalTransactionId ?? null;
        // N1：提取 expiresDate 用于续订延长
        if (tx.expiresDate) {
          expiresDate = new Date(tx.expiresDate);
        }
      } catch { /* 无法解析内层 JWS，用已有信息尽力匹配 */ }
    }

    // 只处理 source=apple 的 Grant
    const appleGrant = originalTxId
      ? await this.prisma.userMembership.findFirst({
          where: { originalTransactionId: originalTxId, source: 'apple' },
        })
      : null;

    if (!appleGrant && this.needsGrant(type)) {
      this.logger.warn(`No Apple grant found for notification type=${type}, originalTxId=${originalTxId}`);
      return;
    }

    const signedDate = payload.data?.signedDate ? new Date(payload.data.signedDate) : new Date();

    switch (type) {
      case 'SUBSCRIBED':
        // 新订阅——由 POST /transactions 处理，此处仅记录
        this.logger.log(`SUBSCRIBED: originalTxId=${originalTxId}`);
        break;

      case 'DID_RENEW':
        if (appleGrant) await this.applyRenewal(appleGrant.id, txId, expiresDate);
        break;

      case 'DID_CHANGE_RENEWAL_STATUS':
        if (appleGrant) await this.applyRenewalStatusChange(appleGrant.id, payload.subtype);
        break;

      case 'DID_FAIL_TO_RENEW':
        if (appleGrant) await this.applyFailureToRenew(appleGrant.id);
        break;

      case 'DID_RECOVER':
        if (appleGrant) await this.applyRecovery(appleGrant.id);
        break;

      case 'GRACE_PERIOD_EXPIRED':
      case 'EXPIRED':
        if (appleGrant) await this.applyExpiration(appleGrant.id, signedDate);
        break;

      case 'REFUND':
      case 'REVOKE':
        if (appleGrant) await this.applyRevocation(appleGrant.id, signedDate, payload.subtype);
        break;
    }
  }

  // ── Lifecycle Actions ──

  private needsGrant(type: string): boolean {
    return !['SUBSCRIBED'].includes(type); // SUBSCRIBED 由交易上报处理
  }

  private async applyRenewal(grantId: string, newTxId: string | null, expiresDate: Date | null) {
    const data: any = { status: 'active' };
    if (newTxId) data.transactionId = newTxId;
    // N1：更新 expiresAt/expiresDate 为续订后的新到期时间
    if (expiresDate) {
      data.expiresAt = expiresDate;
      data.expiresDate = expiresDate;
    }
    await this.prisma.userMembership.update({ where: { id: grantId }, data });
    this.logger.log(`DID_RENEW: grant=${grantId} renewed, expiresAt=${expiresDate?.toISOString() ?? 'unchanged'}`);
  }

  private async applyRenewalStatusChange(grantId: string, subtype?: string) {
    // subtype = 'AUTO_RENEW_DISABLED' | 'AUTO_RENEW_ENABLED'
    const newStatus = subtype === 'AUTO_RENEW_ENABLED' ? 'on' : 'off';
    await this.prisma.userMembership.update({
      where: { id: grantId },
      data: { autoRenewStatus: newStatus },
    });
    this.logger.log(`DID_CHANGE_RENEWAL_STATUS: grant=${grantId} autoRenew=${newStatus}`);
  }

  private async applyFailureToRenew(grantId: string) {
    const grant = await this.prisma.userMembership.findUnique({ where: { id: grantId } });
    if (!grant) return;

    // 如果仍在 grace_period 内，保持 grace_period；否则进入 billing_retry
    const now = new Date();
    const inGrace = grant.gracePeriodExpiresAt && grant.gracePeriodExpiresAt > now;
    await this.prisma.userMembership.update({
      where: { id: grantId },
      data: { status: inGrace ? 'grace_period' : 'billing_retry' },
    });
    this.logger.log(`DID_FAIL_TO_RENEW: grant=${grantId} status=${inGrace ? 'grace_period' : 'billing_retry'}`);
  }

  private async applyRecovery(grantId: string) {
    await this.prisma.userMembership.update({
      where: { id: grantId },
      data: { status: 'active' },
    });
    this.logger.log(`DID_RECOVER: grant=${grantId} restored to active`);
  }

  private async applyExpiration(grantId: string, signedDate: Date) {
    // 乱序保护：旧事件不覆盖新状态
    const grant = await this.prisma.userMembership.findUnique({ where: { id: grantId } });
    if (grant?.expiresAt && grant.expiresAt > signedDate) {
      this.logger.warn(`EXPIRED notification older than current expiresAt, ignoring`);
      return;
    }
    await this.prisma.userMembership.update({
      where: { id: grantId },
      data: { status: 'expired' },
    });
    this.logger.log(`EXPIRED: grant=${grantId}`);
  }

  private async applyRevocation(grantId: string, signedDate: Date, reason?: string) {
    const grant = await this.prisma.userMembership.findUnique({ where: { id: grantId } });
    if (grant?.revocationDate && grant.revocationDate > signedDate) {
      this.logger.warn(`REVOKE notification older than current revocationDate, ignoring`);
      return;
    }
    await this.prisma.userMembership.update({
      where: { id: grantId },
      data: {
        status: 'revoked',
        revocationDate: signedDate,
        revocationReason: reason ?? null,
      },
    });
    this.logger.log(`REVOKE: grant=${grantId} reason=${reason}`);
  }
}
