/**
 * Signing-order domain helper — multi-signatory-coordination (wave-12).
 *
 * Owns the rule for simultaneous vs sequential dispatch.
 * Two wiring points (owned upstream) MUST call recipientsForDispatch:
 *   (a) POST /api/contracts/:id/send  — initial dispatch
 *   (b) POST /api/sign/:token         — after recording a signature, to email
 *       the now-current next signatory
 *
 * Simultaneous:  all signatories have order === 1
 * Sequential:    distinct orders (1, 2, 3...); only the current turn signs
 */

export interface SignatoryOrderRow {
  id: string
  order: number
  signedAt: Date | null
  declinedAt: Date | null
}

/**
 * True when orders are not all 1 (i.e. distinct orders present for at least
 * two signatories). Simultaneous = every signatory has order === 1.
 */
export function isSequential(signatories: SignatoryOrderRow[]): boolean {
  if (signatories.length === 0) return false
  const allOne = signatories.every((s) => s.order === 1)
  return !allOne
}

/**
 * The lowest `order` value among signatories who have not yet signed and have
 * not declined. This is the "current turn" in sequential signing.
 * Returns 1 if all have signed/declined (no one waiting).
 */
export function currentTurnOrder(signatories: SignatoryOrderRow[]): number {
  const pending = signatories.filter((s) => s.signedAt === null && s.declinedAt === null)
  if (pending.length === 0) return 1
  return Math.min(...pending.map((s) => s.order))
}

/**
 * True when this signatory should currently be able to sign:
 *   - Always true in simultaneous mode (all order=1)
 *   - In sequential mode: only when their order === currentTurnOrder
 */
export function isSignatoryTurn(
  signatory: SignatoryOrderRow,
  all: SignatoryOrderRow[],
): boolean {
  if (!isSequential(all)) return true
  return signatory.order === currentTurnOrder(all)
}

/**
 * Returns the signatories who should receive a signing email right now:
 *   - Simultaneous: all unsigned/undeclined signatories
 *   - Sequential: only those whose order === currentTurnOrder (and unsigned/undeclined)
 */
export function recipientsForDispatch(all: SignatoryOrderRow[]): SignatoryOrderRow[] {
  const unsigned = all.filter((s) => s.signedAt === null && s.declinedAt === null)
  if (!isSequential(all)) return unsigned
  const turn = currentTurnOrder(all)
  return unsigned.filter((s) => s.order === turn)
}

/**
 * Validate that signing orders are valid:
 *   - All 1 (simultaneous): OK
 *   - All unique (sequential): OK
 *   - Any mix of duplicate non-1 values: throws 422 error
 */
export function assertOrdersValid(orders: number[]): void {
  // All 1 = simultaneous — always valid
  if (orders.every((o) => o === 1)) return

  // Otherwise all must be unique
  const seen = new Set<number>()
  for (const o of orders) {
    if (seen.has(o)) {
      const err = new Error(
        'Signing orders must be unique per contract, or all set to 1 for simultaneous signing',
      )
      ;(err as Error & { status: number }).status = 422
      throw err
    }
    seen.add(o)
  }
}
