// Voided Purchase Notification — fires when a purchase is refunded or chargeback'd.
// Distinct from `subscriptionNotification` (which carries SUBSCRIPTION_REVOKED only
// when access is also revoked alongside the refund). VoidedPurchase fires on the
// underlying order regardless of whether access is revoked.
//
// Payload (per Google docs):
//   {
//     purchaseToken: string,
//     orderId: string,
//     productType: 1 = ONE_TIME, 2 = SUBSCRIPTION,
//     refundType:  1 = FULL,     2 = QUANTITY_BASED
//   }

import { createEvent } from '../../../libs/audit';
import logger from '../../../libs/logger';
import { Subscription } from '../../../store/models';

export const GooglePlayVoidedProductType = {
  ONE_TIME: 1,
  SUBSCRIPTION: 2,
} as const;

export const GooglePlayVoidedRefundType = {
  FULL: 1,
  QUANTITY_BASED: 2,
} as const;

export type GooglePlayVoidedPurchaseNotification = {
  purchaseToken: string;
  orderId: string;
  productType: number;
  refundType: number;
};

export async function handleGooglePlayVoidedPurchase({
  packageName,
  notification,
}: {
  packageName: string;
  notification: GooglePlayVoidedPurchaseNotification;
}): Promise<void> {
  const { purchaseToken, orderId, productType, refundType } = notification;

  logger.info('received google_play voided-purchase notification', {
    packageName,
    orderId,
    productType,
    refundType,
  });

  if (productType !== GooglePlayVoidedProductType.SUBSCRIPTION) {
    // One-time product voids are out of scope here (the one-time IAP flow isn't
    // wired yet). Log and skip so Pub/Sub still ACKs.
    logger.info('google_play voided-purchase: ignoring non-subscription productType', {
      productType,
      orderId,
    });
    return;
  }

  const subscription = await Subscription.findOne({
    where: {
      'payment_details.google_play.purchase_token': purchaseToken,
    } as any,
  });

  if (!subscription) {
    logger.warn('google_play voided-purchase: no local subscription matches purchaseToken', {
      purchaseToken,
      orderId,
    });
    return;
  }

  // Terminate access. Idempotent — already-canceled subs are a no-op.
  // We mirror handleRevoked's pattern from subscription.ts (which currently does
  // the same minimal update + emits the same event; a future iteration should
  // also create a Refund row, but that path needs payment_intent_id wiring for
  // IAP channels which isn't done yet — see TODO below).
  if (subscription.status !== 'canceled' && subscription.status !== 'incomplete_expired') {
    await subscription.update({
      status: 'canceled',
      ended_at: Math.floor(Date.now() / 1000),
      cancelation_details: {
        ...(subscription.cancelation_details ?? { comment: '', feedback: 'other' }),
        reason: 'cancellation_requested',
      },
      metadata: {
        ...(subscription.metadata || {}),
        google_play_voided_order_id: orderId,
        google_play_voided_refund_type: refundType,
      },
    });
    createEvent('Subscription', 'customer.subscription.deleted', subscription).catch(console.error);
  }

  // TODO: create a Refund row for audit. Blocked on payment_intent_id schema —
  // it's NOT NULL but we don't synthesize Stripe-style payment intents for IAP
  // channel purchases. Either relax the column, or maintain a parallel
  // 'platform_voided' record on Subscription.metadata only.
  logger.info('google_play voided-purchase: subscription terminated (Refund row TBD)', {
    subscriptionId: subscription.id,
    orderId,
    refundType,
  });
}
