import { and, eq, gt, isNull, lte, sql } from 'drizzle-orm';
import { createHash, randomUUID } from 'node:crypto';
import { env } from '@/server/env.js';
import { getPaymentProvider } from '@/server/payments/get-provider.js';
import { PaymentErrorCode } from '@/server/payments/provider.js';
import {
  loadPurchasePaymentContext,
  findById as findPurchaseById,
} from '@/server/db/queries/purchases.js';
import { captureCaught } from '@/server/observability/capture.server.js';
import {
  toInventoryDb,
  reserve,
  release,
  isInventoryItemNotFoundError,
  stockReservation,
} from '@/server/stock/inventory-platform.js';
import {
  failOrder,
  claimForCharge,
  isOrderNotChargeableError,
  type OrdersSchema,
} from '@platform-modules/commerce-orders';
import type { Transaction } from '@platform-modules/db';
import type { DrizzleDb, TxDrizzleClient } from '@/server/db/client.js';
import { order, orderLine } from '@/server/db/schema.js';
import {
  CommandClaimLostError,
  claimCommand,
  completeCommand,
  failCommand,
  reclaimCommand,
  renewCommandLease,
} from '@/server/domain/commands/index.js';
import type { CommandRecord } from '@/server/domain/commands/types.js';
import {
  decideCheckoutIntentStart,
  decidePostChargeReserve,
  type CheckoutActor,
} from './machine.js';
import type {
  CheckoutChargeResult,
  CheckoutIntentErrorCode,
  CheckoutIntentResult,
  CheckoutPurchaseContext,
} from './types.js';

export type { CheckoutActor };

const CHECKOUT_FAILURE_CODES = new Set<string>([
  ...Object.values(PaymentErrorCode),
  'NOT_FOUND',
  'FORBIDDEN',
  'INVALID_STATUS',
  'VENDOR_NOT_ONBOARDED',
  'SOLD_OUT',
  'INVALID_QUANTITY',
  'INVALID_AMOUNT',
  'INTERNAL_ERROR',
  'PAYMENT_FAILED',
  'PAYMENT_PROVIDER_ERROR',
]);

function toCheckoutIntentErrorCode(code: string | null): CheckoutIntentErrorCode {
  return code && CHECKOUT_FAILURE_CODES.has(code)
    ? (code as CheckoutIntentErrorCode)
    : 'PAYMENT_FAILED';
}

export interface HandleCheckoutIntentInput {
  purchaseId: string;
  actor: CheckoutActor;
  guestAuthorized: boolean;
}

export interface HandleCheckoutIntentDeps {
  loadPurchase: (purchaseId: string) => Promise<CheckoutPurchaseContext | null>;
  claimIntentCommand: (args: {
    commandKey: string;
    purchaseId: string;
    actor: CheckoutActor;
    financialSnapshot: {
      orderId: string;
      totalAgorot: number;
      quantity: number;
      skuId: string | null;
      vendorAccountId: string;
    };
  }) => Promise<{ fresh: boolean; record: CommandRecord }>;
  reclaimIntentCommand: (
    record: CommandRecord,
  ) => Promise<{ reclaimed: boolean; record: CommandRecord }>;
  renewIntentCommand: (
    record: CommandRecord,
  ) => Promise<{ renewed: boolean; record: CommandRecord }>;
  completeIntentCommand: (args: {
    commandId: string;
    expectedClaimedBy: string;
    expectedClaimGeneration: number;
    purchaseId: string;
    result: CheckoutIntentResult;
  }) => Promise<void>;
  failIntentCommand: (args: {
    commandId: string;
    expectedClaimedBy: string;
    expectedClaimGeneration: number;
    purchaseId: string;
    code: string;
    message: string;
  }) => Promise<void>;
  ensureCustomerId: (purchase: CheckoutPurchaseContext) => Promise<string | null>;
  claimOrderForCharge: (orderId: string) => Promise<void>;
  markOrderFailed: (orderId: string, reason: string) => Promise<void>;
  charge: (args: {
    commandKey: string;
    customerId: string | null;
    purchaseId: string;
    totalAgorot: number;
    vendor: {
      vendorId: string;
      providerAccountId: string;
      dealTitle: string;
    };
    buyer: {
      name: string;
      email?: string;
    };
  }) => Promise<CheckoutChargeResult>;
  hasActiveReservation: (holderRef: string) => Promise<boolean>;
  reserveInventory: (args: { skuId: string; qty: number; holderRef: string }) => Promise<void>;
  releaseInventory: (holderRef: string) => Promise<void>;
}

function isSoldOutError(err: unknown): boolean {
  return (
    (err instanceof Error && err.message.startsWith('oversold:')) ||
    isInventoryItemNotFoundError(err)
  );
}

/** Hashes purchaseId + stable actor identity — never the guest token, which rotates per request and would break replay dedup. */
function toPayloadHash(input: {
  purchaseId: string;
  actor: CheckoutActor;
  financialSnapshot: {
    orderId: string;
    totalAgorot: number;
    quantity: number;
    skuId: string | null;
    vendorAccountId: string;
  };
}): string {
  const identity = input.actor.kind === 'user' ? input.actor.userId : input.purchaseId;
  return createHash('sha256')
    .update(
      JSON.stringify({
        purchaseId: input.purchaseId,
        actorKind: input.actor.kind,
        identity,
        financialSnapshot: input.financialSnapshot,
      }),
    )
    .digest('hex');
}

function replayStoredResult(record: CommandRecord): CheckoutIntentResult | null {
  if (record.status === 'COMPLETED') {
    const result = record.resultPayload as CheckoutIntentResult | null;
    return result?.ok === true || result?.ok === false ? result : null;
  }

  if (record.status === 'FAILED') {
    const status =
      record.failureCode === 'SOLD_OUT' || record.failureCode === 'INVALID_STATUS'
        ? 409
        : record.failureCode === 'PAYMENT_PROVIDER_ERROR' ||
            record.failureCode === 'UNKNOWN' ||
            record.failureCode === 'PROVIDER_ERROR' ||
            record.failureCode === 'RATE_LIMITED'
          ? 502
          : 402;
    return {
      ok: false,
      code: toCheckoutIntentErrorCode(record.failureCode),
      error: record.failureMessage ?? 'Payment failed',
      status,
    };
  }

  return null;
}

async function compensateFailure(
  deps: Pick<HandleCheckoutIntentDeps, 'markOrderFailed'>,
  orderId: string,
  reason: string,
): Promise<void> {
  try {
    await deps.markOrderFailed(orderId, reason);
  } catch (err) {
    captureCaught(err as Error, {
      scope: 'pages.api.checkout.intent.compensateFailOrder',
      severity: 'warning',
      extra: { orderId, reason },
    });
    throw err;
  }
}

async function releaseInventorySafely(
  deps: Pick<HandleCheckoutIntentDeps, 'releaseInventory'>,
  holderRef: string,
): Promise<void> {
  try {
    await deps.releaseInventory(holderRef);
  } catch (err) {
    captureCaught(err as Error, {
      scope: 'pages.api.checkout.intent.compensateReleaseInventory',
      severity: 'warning',
      extra: { holderRef },
    });
    throw err;
  }
}

async function compensateCheckoutFailure(
  deps: Pick<HandleCheckoutIntentDeps, 'releaseInventory' | 'markOrderFailed'>,
  input: { holderRef?: string; orderId: string; reason: string },
): Promise<void> {
  const failures: unknown[] = [];
  if (input.holderRef) {
    try {
      await releaseInventorySafely(deps, input.holderRef);
    } catch (err) {
      failures.push(err);
    }
  }
  try {
    await compensateFailure(deps, input.orderId, input.reason);
  } catch (err) {
    failures.push(err);
  }
  if (failures.length === 1) throw failures[0];
  if (failures.length > 1) {
    throw new AggregateError(failures, 'Checkout compensation failed');
  }
}

export async function handleCheckoutIntent(
  deps: HandleCheckoutIntentDeps,
  input: HandleCheckoutIntentInput,
): Promise<CheckoutIntentResult> {
  let purchase: CheckoutPurchaseContext | null;
  try {
    purchase = await deps.loadPurchase(input.purchaseId);
  } catch (err) {
    captureCaught(err as Error, {
      scope: 'pages.api.checkout.intent.loadPurchase',
      severity: 'error',
      extra: { purchaseId: input.purchaseId },
    });
    return {
      ok: false,
      code: 'INTERNAL_ERROR',
      error: 'Internal error',
      status: 500,
    };
  }
  if (!purchase) {
    return {
      ok: false,
      code: 'NOT_FOUND',
      error: 'Purchase not found',
      status: 404,
    };
  }

  const start = decideCheckoutIntentStart({
    purchaseUserId: purchase.userId,
    actor: input.actor,
    guestAuthorized: input.guestAuthorized,
    paymentStatus: purchase.paymentStatus,
    vendorReady: Boolean(purchase.vendor.stripeAccountId && purchase.vendor.chargesEnabled),
    orderStatus: purchase.orderStatus,
  });
  if (!start.ok) {
    return {
      ok: false,
      code: start.code,
      error: start.error,
      status: start.code === 'FORBIDDEN' ? 403 : start.code === 'VENDOR_NOT_ONBOARDED' ? 422 : 409,
    };
  }

  if (!Number.isSafeInteger(purchase.quantity) || purchase.quantity <= 0) {
    return {
      ok: false,
      code: 'INVALID_QUANTITY',
      error: 'Purchase quantity is invalid',
      status: 422,
    };
  }

  if (!Number.isSafeInteger(purchase.totalAgorot) || purchase.totalAgorot <= 0) {
    return {
      ok: false,
      code: 'INVALID_AMOUNT',
      error: 'Purchase total is invalid',
      status: 422,
    };
  }

  const financialSnapshot = {
    orderId: purchase.orderId,
    totalAgorot: purchase.totalAgorot,
    quantity: purchase.quantity,
    skuId: purchase.deal.dealSkuId,
    vendorAccountId: purchase.vendor.stripeAccountId!,
  };

  let claimed = await deps.claimIntentCommand({
    commandKey: purchase.orderClientIdempotencyKey,
    purchaseId: purchase.purchaseId,
    actor: input.actor,
    financialSnapshot,
  });
  if (!claimed.fresh) {
    const replay = replayStoredResult(claimed.record);
    if (replay) {
      return replay.ok ? { ...replay, replayed: true } : replay;
    }
    const reclaimed = await deps.reclaimIntentCommand(claimed.record);
    if (!reclaimed.reclaimed) {
      return {
        ok: false,
        code: 'INVALID_STATUS',
        error: 'Checkout intent already in progress',
        status: 409,
      };
    }
    claimed = { fresh: true, record: reclaimed.record };
  }

  const renewClaim = async () => {
    const renewed = await deps.renewIntentCommand(claimed.record);
    if (!renewed.renewed) {
      throw new CommandClaimLostError(claimed.record.id, 'renew');
    }
    claimed = { fresh: true, record: renewed.record };
  };

  if (start.shouldClaimOrder) {
    try {
      await deps.claimOrderForCharge(purchase.orderId);
    } catch (err) {
      if (!isOrderNotChargeableError(err)) throw err;
      captureCaught(err as Error, {
        scope: 'pages.api.checkout.intent.claimForCharge',
        severity: 'warning',
        extra: { purchaseId: input.purchaseId },
      });
      const result = {
        ok: false,
        code: 'INVALID_STATUS',
        error: 'Order is not ready for payment',
        status: 409,
      } satisfies CheckoutIntentResult;
      await deps.failIntentCommand({
        commandId: claimed.record.id,
        expectedClaimedBy: claimed.record.claimedBy,
        expectedClaimGeneration: claimed.record.claimGeneration,
        purchaseId: purchase.purchaseId,
        code: result.code,
        message: result.error,
      });
      return result;
    }
  }

  const shouldReserve =
    purchase.deal.dealSkuId &&
    (purchase.deal.dealType === 'ITEM' || purchase.deal.dealType === 'COUPON');
  const reservationExists = await deps.hasActiveReservation(purchase.purchaseId);

  if (shouldReserve && !reservationExists) {
    try {
      await deps.reserveInventory({
        skuId: purchase.deal.dealSkuId!,
        qty: purchase.quantity ?? 1,
        holderRef: purchase.purchaseId,
      });
    } catch (err) {
      if (isSoldOutError(err)) {
        await compensateFailure(deps, purchase.orderId, 'inventory_not_found');
        const result = {
          ok: false,
          code: 'SOLD_OUT',
          error: 'Item is sold out',
          status: 409,
        } satisfies CheckoutIntentResult;
        await deps.failIntentCommand({
          commandId: claimed.record.id,
          expectedClaimedBy: claimed.record.claimedBy,
          expectedClaimGeneration: claimed.record.claimGeneration,
          purchaseId: purchase.purchaseId,
          code: result.code,
          message: result.error,
        });
        return result;
      }
      captureCaught(err as Error, {
        scope: 'pages.api.checkout.intent.reserveInventory',
        severity: 'error',
        extra: { purchaseId: purchase.purchaseId },
      });
      const result = {
        ok: false,
        code: 'INTERNAL_ERROR',
        error: 'Internal error',
        status: 500,
      } satisfies CheckoutIntentResult;
      return result;
    }
  }

  let customerId: string | null;
  try {
    await renewClaim();
    customerId = await deps.ensureCustomerId(purchase);
    await renewClaim();
  } catch (err) {
    if (err instanceof CommandClaimLostError) throw err;
    captureCaught(err as Error, {
      scope: 'pages.api.checkout.intent.paymentProvider',
      severity: 'error',
      extra: { purchaseId: purchase.purchaseId },
    });
    await compensateCheckoutFailure(deps, {
      ...(shouldReserve ? { holderRef: purchase.purchaseId } : {}),
      orderId: purchase.orderId,
      reason: 'PAYMENT_PROVIDER_ERROR',
    });
    const result = {
      ok: false,
      code: 'PAYMENT_PROVIDER_ERROR',
      error: 'Payment provider unavailable',
      status: 502,
    } satisfies CheckoutIntentResult;
    await deps.failIntentCommand({
      commandId: claimed.record.id,
      expectedClaimedBy: claimed.record.claimedBy,
      expectedClaimGeneration: claimed.record.claimGeneration,
      purchaseId: purchase.purchaseId,
      code: result.code,
      message: result.error,
    });
    return result;
  }

  let charge: CheckoutChargeResult;
  try {
    await renewClaim();
    charge = await deps.charge({
      commandKey: purchase.orderClientIdempotencyKey,
      customerId,
      purchaseId: purchase.purchaseId,
      totalAgorot: purchase.totalAgorot,
      vendor: {
        vendorId: purchase.vendor.id,
        providerAccountId: purchase.vendor.stripeAccountId!,
        dealTitle: purchase.deal.title,
      },
      buyer: purchase.buyer,
    });
    await renewClaim();
  } catch (err) {
    captureCaught(err as Error, {
      scope: 'pages.api.checkout.intent.paymentProvider',
      severity: 'error',
      extra: { purchaseId: purchase.purchaseId },
    });
    return {
      ok: false,
      code: 'PAYMENT_PROVIDER_ERROR',
      error: 'Payment provider unavailable',
      status: 502,
    } satisfies CheckoutIntentResult;
  }

  if (!charge.ok) {
    if (
      charge.code === 'UNKNOWN' ||
      charge.code === 'PROVIDER_ERROR' ||
      charge.code === 'RATE_LIMITED'
    ) {
      return {
        ok: false,
        code: charge.code,
        error: charge.message ?? 'Payment provider unavailable',
        status: 502,
      } satisfies CheckoutIntentResult;
    }
    await compensateCheckoutFailure(deps, {
      ...(shouldReserve ? { holderRef: purchase.purchaseId } : {}),
      orderId: purchase.orderId,
      reason: charge.code,
    });
    const result = {
      ok: false,
      code: charge.code,
      error: charge.message ?? 'Payment failed',
      status: 402,
    } satisfies CheckoutIntentResult;
    await deps.failIntentCommand({
      commandId: claimed.record.id,
      expectedClaimedBy: claimed.record.claimedBy,
      expectedClaimGeneration: claimed.record.claimGeneration,
      purchaseId: purchase.purchaseId,
      code: result.code,
      message: result.error,
    });
    return result;
  }

  const decided = decidePostChargeReserve({
    chargeStatus: charge.status,
    providerPaymentId: charge.providerPaymentId,
    clientSecret:
      charge.status === 'requires_client_confirmation' ? charge.clientSecret : undefined,
    reservationExists,
  });

  const result = {
    ok: true,
    data: decided.data,
    ...(decided.replayed ? { replayed: true } : {}),
  } satisfies CheckoutIntentResult;
  await deps.completeIntentCommand({
    commandId: claimed.record.id,
    expectedClaimedBy: claimed.record.claimedBy,
    expectedClaimGeneration: claimed.record.claimGeneration,
    purchaseId: purchase.purchaseId,
    result,
  });
  return result;
}

export function createCheckoutIntentDeps(db: TxDrizzleClient): HandleCheckoutIntentDeps {
  return {
    async loadPurchase(purchaseId) {
      const paymentCtx = await loadPurchasePaymentContext(db, purchaseId, env.PII_KEY);

      const purchase = await findPurchaseById(db, purchaseId);
      const [lineRow] = await db
        .select({
          orderId: orderLine.orderId,
          orderStatus: order.status,
          orderClientIdempotencyKey: order.idempotencyKey,
        })
        .from(orderLine)
        .innerJoin(order, eq(order.id, orderLine.orderId))
        .where(eq(orderLine.id, purchaseId))
        .limit(1);

      if (!purchase || !lineRow) return null;

      return {
        ...paymentCtx,
        paymentStatus: purchase.paymentStatus,
        orderId: lineRow.orderId,
        orderStatus: lineRow.orderStatus,
        orderClientIdempotencyKey: lineRow.orderClientIdempotencyKey,
      };
    },

    async claimIntentCommand(args) {
      return claimCommand(db, {
        commandKey: args.commandKey,
        commandType: 'checkout.intent',
        aggregateType: 'purchase',
        aggregateId: args.purchaseId,
        payloadHash: toPayloadHash({
          purchaseId: args.purchaseId,
          actor: args.actor,
          financialSnapshot: args.financialSnapshot,
        }),
        payload:
          args.actor.kind === 'user'
            ? {
                purchaseId: args.purchaseId,
                actorKind: 'user',
                userId: args.actor.userId,
                financialSnapshot: args.financialSnapshot,
              }
            : {
                purchaseId: args.purchaseId,
                actorKind: 'guest',
                financialSnapshot: args.financialSnapshot,
              },
        claimedBy: `checkout.intent:${randomUUID()}`,
      });
    },

    async reclaimIntentCommand(record) {
      return reclaimCommand(db, {
        commandId: record.id,
        claimedBy: `checkout.intent:${randomUUID()}`,
        expectedCommandKey: record.commandKey,
        expectedCommandType: record.commandType,
        expectedAggregateType: record.aggregateType,
        expectedAggregateId: record.aggregateId,
        expectedPayloadHash: record.payloadHash,
      });
    },

    async renewIntentCommand(record) {
      return renewCommandLease(db, {
        commandId: record.id,
        expectedClaimedBy: record.claimedBy,
        expectedClaimGeneration: record.claimGeneration,
      });
    },

    async completeIntentCommand(args) {
      await completeCommand(db, {
        commandId: args.commandId,
        expectedClaimedBy: args.expectedClaimedBy,
        expectedClaimGeneration: args.expectedClaimGeneration,
        completedAt: new Date(),
        resultPayload: args.result,
        outbox: {
          eventType: 'checkout.intent.completed',
          payload: { purchaseId: args.purchaseId },
        },
      });
    },

    async failIntentCommand(args) {
      await failCommand(db, {
        commandId: args.commandId,
        expectedClaimedBy: args.expectedClaimedBy,
        expectedClaimGeneration: args.expectedClaimGeneration,
        failedAt: new Date(),
        failureCode: args.code,
        failureMessage: args.message,
        outbox: {
          eventType: 'checkout.intent.failed',
          payload: { purchaseId: args.purchaseId, code: args.code },
        },
      });
    },

    async ensureCustomerId(purchase) {
      const userId = purchase.userId;
      if (!userId) return null;
      const provider = await getPaymentProvider(env);
      return provider.ensureCustomerId({ userId, buyer: purchase.buyer });
    },

    async claimOrderForCharge(orderId) {
      await (db as unknown as TxDrizzleClient).transaction(async (tx) => {
        const orderTx = tx as unknown as Transaction<OrdersSchema>;
        await claimForCharge(orderTx, orderId);
      });
    },

    async markOrderFailed(orderId, reason) {
      await (db as unknown as TxDrizzleClient).transaction(async (tx) => {
        await failOrder(tx as unknown as Transaction<OrdersSchema>, orderId, reason);
      });
    },

    async charge(args) {
      const provider = await getPaymentProvider(env);
      return provider.charge({
        purchaseId: args.purchaseId,
        ...(args.customerId ? { customerId: args.customerId } : {}),
        totalAgorot: args.totalAgorot,
        vendor: args.vendor,
        buyer: args.buyer,
      });
    },

    async hasActiveReservation(holderRef) {
      return toInventoryDb(db as unknown as DrizzleDb).transaction(async (tx) => {
        const expired = await tx
          .select({ id: stockReservation.id })
          .from(stockReservation)
          .where(
            and(
              eq(stockReservation.holderRef, holderRef),
              isNull(stockReservation.consumedAt),
              isNull(stockReservation.releasedAt),
              lte(stockReservation.expiresAt, sql`CURRENT_TIMESTAMP`),
            ),
          );
        for (const row of expired) await release(tx, row.id);
        const [active] = await tx
          .select({ id: stockReservation.id })
          .from(stockReservation)
          .where(
            and(
              eq(stockReservation.holderRef, holderRef),
              isNull(stockReservation.consumedAt),
              isNull(stockReservation.releasedAt),
              gt(stockReservation.expiresAt, sql`CURRENT_TIMESTAMP`),
            ),
          )
          .limit(1);
        return Boolean(active);
      });
    },

    async reserveInventory(args) {
      await toInventoryDb(db as unknown as DrizzleDb).transaction((tx) =>
        reserve(tx, {
          skuId: args.skuId,
          qty: args.qty,
          holderRef: args.holderRef,
          now: new Date(),
        }),
      );
    },

    async releaseInventory(holderRef) {
      const rows = await db
        .select({ id: stockReservation.id })
        .from(stockReservation)
        .where(
          and(
            eq(stockReservation.holderRef, holderRef),
            isNull(stockReservation.consumedAt),
            isNull(stockReservation.releasedAt),
          ),
        );
      await toInventoryDb(db as unknown as DrizzleDb).transaction(async (tx) => {
        for (const row of rows) await release(tx, row.id);
      });
    },
  };
}
