import { ValidationError } from '../../errors'
import { Amount } from '../common'

import {
  Account,
  BaseTransaction,
  isAccount,
  isAmount,
  isNumber,
  validateBaseTransaction,
  validateOptionalField,
  validateRequiredField,
} from './common'

/**
 * Create a unidirectional channel and fund it. The address sending
 * this transaction becomes the "source address" of the payment channel.
 *
 * @category Transaction Models
 */
export interface PaymentChannelCreate extends BaseTransaction {
  TransactionType: 'PaymentChannelCreate'
  /**
   * Amount to deduct from the sender's balance and set aside in this channel.
   * While the channel is open, the amount can only go to the Destination
   * address. When the channel closes, any unclaimed amount is returned to
   * the source address's balance.
   */
  Amount: Amount
  /**
   * Address to receive claims against this channel. This is also known as
   * the "destination address" for the channel.
   */
  Destination: Account
  /**
   * Amount of time the source address must wait before closing the channel if
   * it has unclaimed.
   */
  SettleDelay: number
  /**
   * The public key of the key pair the source will use to sign claims against
   * this channel in hexadecimal. This can be any secp256k1 or ed25519 public
   * key.
   */
  PublicKey: string
  /**
   * The time, in seconds since the Ripple Epoch, when this channel expires.
   * Any transaction that would modify the channel after this time closes the
   * channel without otherwise affecting it. This value is immutable; the
   * channel can be closed earlier than this time but cannot remain open after
   * this time.
   */
  CancelAfter?: number
  /**
   * Arbitrary tag to further specify the destination for this payment channel,
   * such as a hosted recipient at the destination address.
   */
  DestinationTag?: number
}

/**
 * Verify the form and type of an PaymentChannelCreate at runtime.
 *
 * @param tx - An PaymentChannelCreate Transaction.
 * @throws When the PaymentChannelCreate is Malformed.
 */
export function validatePaymentChannelCreate(
  tx: Record<string, unknown>,
): void {
  validateBaseTransaction(tx)

  if (tx.Amount === undefined) {
    throw new ValidationError('PaymentChannelCreate: missing Amount')
  }

  if (typeof tx.Amount !== 'string' && !isAmount(tx.Amount)) {
    throw new ValidationError('PaymentChannelCreate: Amount must be an Amount')
  }

  validateRequiredField(tx, 'Destination', isAccount)
  validateOptionalField(tx, 'DestinationTag', isNumber)

  if (tx.SettleDelay === undefined) {
    throw new ValidationError('PaymentChannelCreate: missing SettleDelay')
  }

  if (typeof tx.SettleDelay !== 'number') {
    throw new ValidationError(
      'PaymentChannelCreate: SettleDelay must be a number',
    )
  }

  if (tx.PublicKey === undefined) {
    throw new ValidationError('PaymentChannelCreate: missing PublicKey')
  }

  if (typeof tx.PublicKey !== 'string') {
    throw new ValidationError(
      'PaymentChannelCreate: PublicKey must be a string',
    )
  }

  if (tx.CancelAfter !== undefined && typeof tx.CancelAfter !== 'number') {
    throw new ValidationError(
      'PaymentChannelCreate: CancelAfter must be a number',
    )
  }
}
