import { Column, Entity, Index } from "typeorm";
import { BaseEntity } from "@edr/api-common";
import {
  ClientAction,
  PaymentReferenceType,
  PaymentService,
  ProviderMethod,
  ProviderPaymentStatus,
} from "@edr/types";

/**
 * One payment attempt for one domain order — the platform-wide source of truth for payment
 * state. `reference_id` is a soft reference into the owning app's schema (never a FK; see
 * docs/payment-service/architecture.md §5).
 *
 * Enum-valued columns are stored as varchar (values mirror the shared @edr/types enums) so
 * adding a provider/status never needs an ALTER TYPE migration.
 */
@Entity({ name: "payment_intent" })
// One ACTIVE intent per domain order; FAILED/CANCELLED attempts may accumulate as audit rows.
@Index(
  "uq_payment_intent_active_reference",
  ["service", "referenceType", "referenceId"],
  {
    unique: true,
    where: `status NOT IN ('FAILED','CANCELLED') AND deleted_at IS NULL`,
  },
)
@Index("idx_payment_intent_sweep", ["status", "updatedAt"])
@Index("idx_payment_intent_idempotency", ["service", "idempotencyKey"])
export class PaymentIntent extends BaseEntity {
  /** Owning domain app — routing discriminator for notifications. */
  @Column({ name: "service", type: "varchar", length: 16 })
  service!: PaymentService;

  @Column({ name: "reference_type", type: "varchar", length: 16 })
  referenceType!: PaymentReferenceType;

  /** Domain order id (booking/shipment). Soft reference — no cross-schema FK. */
  @Column({ name: "reference_id", type: "varchar", length: 64 })
  referenceId!: string;

  /** Provider-facing reference, prefixed PSG-/FRT- so webhooks route before a DB lookup. */
  @Column({
    name: "merchant_order_id",
    type: "varchar",
    length: 64,
    unique: true,
  })
  merchantOrderId!: string;

  @Column({ name: "provider", type: "varchar", length: 16 })
  provider!: ProviderMethod;

  /** Provider-side order/session id (prepay id, HPP orderId, …). */
  @Column({
    name: "provider_order_id",
    type: "varchar",
    length: 128,
    nullable: true,
  })
  providerOrderId?: string | null;

  /** Final provider transaction id, set on terminal success. */
  @Index("idx_payment_intent_provider_txn")
  @Column({
    name: "provider_txn_id",
    type: "varchar",
    length: 128,
    nullable: true,
  })
  providerTxnId?: string | null;

  /** App-asserted authoritative amount (real/major price; may be fractional, e.g. 10.50). */
  @Column({ name: "amount_minor", type: "double precision" })
  amountMinor!: number;

  /** Provider-reported amount; reconciled against amount_minor (e.g. Waafi truncates decimals). */
  @Column({
    name: "confirmed_amount_minor",
    type: "double precision",
    nullable: true,
  })
  confirmedAmountMinor?: number | null;

  @Column({ name: "currency", type: "varchar", length: 8 })
  currency!: string;

  /** State machine: REQUIRES_ACTION → PROCESSING → SUCCEEDED | FAILED | CANCELLED (absorbing). */
  @Column({
    name: "status",
    type: "varchar",
    length: 24,
    default: ProviderPaymentStatus.REQUIRES_ACTION,
  })
  status!: ProviderPaymentStatus;

  /** Redirect/launch payload returned to the app for the user to complete payment. */
  @Column({ name: "client_action", type: "jsonb", nullable: true })
  clientAction?: ClientAction | null;

  @Column({ name: "failure_code", type: "varchar", length: 64, nullable: true })
  failureCode?: string | null;

  @Column({ name: "failure_message", type: "text", nullable: true })
  failureMessage?: string | null;

  /** Caller-supplied initiate dedupe key (in addition to the per-reference upsert). */
  @Column({
    name: "idempotency_key",
    type: "varchar",
    length: 128,
    nullable: true,
  })
  idempotencyKey?: string | null;

  @Column({ name: "expires_at", type: "timestamptz", nullable: true })
  expiresAt?: Date | null;

  @Column({ name: "paid_at", type: "timestamptz", nullable: true })
  paidAt?: Date | null;

  /** Audit copy of the provider initiation request/response (secrets redacted upstream). */
  @Column({ name: "raw_initiation", type: "jsonb", nullable: true })
  rawInitiation?: Record<string, unknown> | null;
}

/** Statuses that keep the per-reference unique index "active" (block a new intent). */
export const ACTIVE_INTENT_STATUSES = [
  ProviderPaymentStatus.REQUIRES_ACTION,
  ProviderPaymentStatus.PROCESSING,
  ProviderPaymentStatus.SUCCEEDED,
] as const;

export const TERMINAL_INTENT_STATUSES = [
  ProviderPaymentStatus.SUCCEEDED,
  ProviderPaymentStatus.FAILED,
  ProviderPaymentStatus.CANCELLED,
] as const;
