/**
 * The App Store Server Notifications V2 webhook: Apple pushing the truth
 * about refunds and revocations to the Worker.
 *
 * The route is unauthenticated by design. Apple does not sign requests with a
 * shared secret; it signs the payload itself. The body is one JWS
 * (signedPayload) wrapping another (data.signedTransactionInfo), both
 * verified against the same pinned Apple Root CA - G3 as a StoreKit
 * transaction. A forged POST fails the signature and gets a 401; the
 * signature is the authentication.
 */
import { verifyAppleJWS } from "./applejws.ts";
import {
  revocationFromNotification,
  type RevocationStore,
} from "./revocation.ts";

export interface DecodedNotification {
  notificationType?: string;
  transaction: { originalTransactionId?: string };
}

/** Verifier signature, so tests can swap in a fake and skip real certificates. */
export type NotificationVerifier = (
  signedPayload: string,
  appleRootSha256: string,
) => Promise<DecodedNotification>;

export const verifyNotification: NotificationVerifier = async (
  signedPayload,
  appleRootSha256,
) => {
  const payload = (await verifyAppleJWS(signedPayload, appleRootSha256)) as {
    notificationType?: string;
    data?: { signedTransactionInfo?: string };
  };
  const inner = payload.data?.signedTransactionInfo;
  const transaction = inner
    ? ((await verifyAppleJWS(inner, appleRootSha256)) as {
        originalTransactionId?: string;
      })
    : {};
  return { notificationType: payload.notificationType, transaction };
};

export async function handleNotification(
  request: Request,
  appleRootSha256: string,
  verifier: NotificationVerifier,
  store: RevocationStore,
): Promise<Response> {
  let signedPayload: unknown;
  try {
    ({ signedPayload } = (await request.json()) as {
      signedPayload?: unknown;
    });
  } catch {
    return new Response("malformed notification", { status: 400 });
  }
  if (typeof signedPayload !== "string") {
    return new Response("malformed notification", { status: 400 });
  }

  let decoded: DecodedNotification;
  try {
    decoded = await verifier(signedPayload, appleRootSha256);
  } catch {
    // A failed signature is a 401 so App Store Connect retries a transient
    // problem; a forged payload never reaches the store.
    return new Response("invalid signature", { status: 401 });
  }

  const action = revocationFromNotification(
    decoded.notificationType,
    decoded.transaction,
  );
  if (action?.kind === "revoke") {
    await store.revoke(action.originalTransactionId, action.reason);
  } else if (action?.kind === "unrevoke") {
    await store.unrevoke(action.originalTransactionId);
  }

  // 200 for processed and ignored alike: Apple only needs delivery confirmed.
  return new Response(null, { status: 200 });
}
