import { Injectable, Logger, BadRequestException, ForbiddenException, ConflictException } 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-05: App Store Transaction Service
 *
 * 接收已验证的交易 JWS payload，校验业务规则，原子投影会员权益。
 */

export interface TransactionPayload {
  transactionId: string;
  originalTransactionId: string;
  bundleId: string;
  productId: string;
  environment: string;
  appAccountToken?: string;
  purchaseDate: number;
  originalPurchaseDate: number;
  expiresDate?: number;
  revocationDate?: number;
  revocationReason?: string;
  transactionReason?: string;
  webOrderLineItemId?: string;
}

interface SubmitResult {
  transactionId: string;
  membershipId: string;
  status: 'created' | 'existing';
}

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

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

  async submitTransaction(
    userId: string,
    signedTransactionInfo: string,
  ): Promise<SubmitResult> {
    // 1. 验证 JWS
    const payload = await this.verifier.verify(signedTransactionInfo);
    // N5：rawPayloadHash 输入为原始 JWS 字符串（与 AppStoreNotification.payloadHash 一致）
    const payloadHash = crypto.createHash('sha256').update(signedTransactionInfo).digest('hex');
    const tx = payload as unknown as TransactionPayload;

    // 2. 事务外快速失败校验
    this.validateBundleId(tx);
    await this.validateProductId(tx);    // N3: 查 StoreProduct 表
    this.validateEnvironment(tx);        // N2: 匹配配置预期环境
    await this.validateAppAccountToken(userId, tx);  // N4: timingSafeEqual
    this.validateNotRevokedOrExpired(tx);
    await this.validateOriginalTransactionOwnership(userId, tx.originalTransactionId);

    // 3. N1: 原子投影 + 事务内幂等（catch P2002）
    try {
      return await this.prisma.$transaction(async (db) => {
        await db.appStoreTransaction.create({
          data: {
            userId,
            productId: tx.productId,
            transactionId: tx.transactionId,
            originalTransactionId: tx.originalTransactionId,
            appAccountToken: tx.appAccountToken ?? '',
            environment: tx.environment,
            purchaseDate: new Date(tx.purchaseDate),
            originalPurchaseDate: new Date(tx.originalPurchaseDate),
            expiresDate: tx.expiresDate ? new Date(tx.expiresDate) : null,
            revocationDate: tx.revocationDate ? new Date(tx.revocationDate) : null,
            revocationReason: tx.revocationReason ?? null,
            signedDate: new Date(),
            transactionReason: tx.transactionReason ?? null,
            webOrderLineItemId: tx.webOrderLineItemId ?? null,
            rawPayloadHash: payloadHash,
          },
        });

        const plan = await db.membershipPlan.findUnique({ where: { code: 'premium' } });
        if (!plan) throw new Error('Premium plan not found');

        const existingMembership = await db.userMembership.findFirst({
          where: { userId, originalTransactionId: tx.originalTransactionId },
        });

        let membershipId: string;

        if (existingMembership) {
          await db.userMembership.update({
            where: { id: existingMembership.id },
            data: {
              status: 'active',
              expiresAt: tx.expiresDate ? new Date(tx.expiresDate) : null,
              expiresDate: tx.expiresDate ? new Date(tx.expiresDate) : null,
              transactionId: tx.transactionId,
              autoRenewStatus: null,
            },
          });
          membershipId = existingMembership.id;
        } else {
          const membership = await db.userMembership.create({
            data: {
              userId, planId: plan.id, source: 'apple', status: 'active',
              productId: tx.productId,
              originalTransactionId: tx.originalTransactionId,
              transactionId: tx.transactionId,
              environment: tx.environment,
              purchaseDate: new Date(tx.purchaseDate),
              expiresAt: tx.expiresDate ? new Date(tx.expiresDate) : null,
              expiresDate: tx.expiresDate ? new Date(tx.expiresDate) : null,
              appAccountToken: tx.appAccountToken ?? null,
            },
          });
          membershipId = membership.id;
        }

        await db.appStoreTransaction.update({
          where: { transactionId: tx.transactionId },
          data: { membershipId },
        });

        return { transactionId: tx.transactionId, membershipId, status: 'created' as const };
      });
    } catch (err: any) {
      // N1: P2002 = transactionId 已存在（并发幂等）
      if (err?.code === 'P2002') {
        const existing = await this.prisma.appStoreTransaction.findUnique({
          where: { transactionId: tx.transactionId },
        });
        if (existing) {
          return {
            transactionId: existing.transactionId,
            membershipId: existing.membershipId ?? '',
            status: 'existing',
          };
        }
      }
      throw err;
    }
  }

  // ── Private validators ──

  private validateBundleId(tx: TransactionPayload): void {
    const expected = this.verifier.getExpectedBundleId();
    if (expected && tx.bundleId !== expected) {
      throw new ForbiddenException(
        `Bundle ID mismatch: expected "${expected}", got "${tx.bundleId}"`,
      );
    }
  }

  // N3: 查询 StoreProduct 表，不再硬编码 Product ID
  private async validateProductId(tx: TransactionPayload): Promise<void> {
    const product = await this.prisma.storeProduct.findFirst({
      where: { platform: 'apple', productId: tx.productId, isActive: true },
      select: { id: true },
    });
    if (!product) {
      throw new BadRequestException(`Unknown or inactive product ID: ${tx.productId}`);
    }
  }

  // N2: 匹配配置预期环境，防止 Sandbox 交易污染 Production
  private validateEnvironment(tx: TransactionPayload): void {
    const expected = this.verifier.getExpectedEnvironment();
    if (expected && tx.environment !== expected) {
      throw new ForbiddenException(
        `Environment mismatch: expected "${expected}", got "${tx.environment}"`,
      );
    }
    // 兜底校验合法值
    if (!['Sandbox', 'Production'].includes(tx.environment)) {
      throw new BadRequestException(`Unknown environment: ${tx.environment}`);
    }
  }

  // N4: timingSafeEqual 防时序攻击
  private async validateAppAccountToken(userId: string, tx: TransactionPayload): Promise<void> {
    const user = await this.prisma.user.findUnique({
      where: { id: userId },
      select: { appAccountToken: true },
    });
    if (!user?.appAccountToken) {
      throw new ForbiddenException('User has no appAccountToken');
    }

    const expected = Buffer.from(user.appAccountToken);
    const actual = Buffer.from(tx.appAccountToken ?? '');

    if (expected.length !== actual.length ||
        !crypto.timingSafeEqual(expected, actual)) {
      throw new ForbiddenException(
        'appAccountToken mismatch: transaction does not belong to this user',
      );
    }
  }

  private validateNotRevokedOrExpired(tx: TransactionPayload): void {
    if (tx.revocationDate) {
      throw new BadRequestException('Transaction has been revoked');
    }
    if (tx.expiresDate && Date.now() > tx.expiresDate) {
      throw new BadRequestException('Transaction has expired');
    }
  }

  private async validateOriginalTransactionOwnership(
    userId: string,
    originalTransactionId: string,
  ): Promise<void> {
    const existing = await this.prisma.userMembership.findFirst({
      where: { originalTransactionId },
      select: { userId: true },
    });
    if (existing && existing.userId !== userId) {
      throw new ConflictException('APPLE_SUBSCRIPTION_ALREADY_BOUND');
    }
  }
}
