import { GenericDocument, Field } from './GenericDocument';

/// All possible root document types.
export type CheckDocumentModelRootType =
  | typeof USACheckDocumentType
  | typeof UAECheckDocumentType
  | typeof FRACheckDocumentType
  | typeof ISRCheckDocumentType
  | typeof KWTCheckDocumentType
  | typeof AUSCheckDocumentType
  | typeof INDCheckDocumentType
  | typeof CANCheckDocumentType
  | typeof UnknownCheckDocumentType;

export const CheckDocumentType = 'Check';
export const USACheckDocumentType = 'USACheck';
export const UAECheckDocumentType = 'UAECheck';
export const FRACheckDocumentType = 'FRACheck';
export const ISRCheckDocumentType = 'ISRCheck';
export const KWTCheckDocumentType = 'KWTCheck';
export const AUSCheckDocumentType = 'AUSCheck';
export const INDCheckDocumentType = 'INDCheck';
export const CANCheckDocumentType = 'CANCheck';
export const UnknownCheckDocumentType = 'UnknownCheck';

/** Check Document */
export abstract class Check {
  private _document: GenericDocument;

  get document(): GenericDocument {
    return this._document;
  }

  constructor(document: GenericDocument) {
    if (document.type.name !== this.requiredDocumentType()) {
      throw new Error(
        `Expected document type ${this.requiredDocumentType()}, got ${document.type.name}`
      );
    }

    this._document = document;
  }

  abstract requiredDocumentType(): string;

  /** type of check font */
  get fontType(): Field | undefined {
    return this.document.fields.find((f: Field) => f.type.name === 'FontType');
  }

  /** Detected raw string */
  get rawString(): Field {
    return this.document.fields.find((f: Field) => f.type.name === 'RawString') as Field;
  }
}

/** A check compatible with the ASC X9 standard used in the USA */
export class USACheck extends Check {
  constructor(document: GenericDocument) {
    super(document);
  }

  requiredDocumentType(): string {
    return USACheckDocumentType;
  }

  /** Account number */
  get accountNumber(): Field {
    return this.document.fields.find((f: Field) => f.type.name === 'AccountNumber') as Field;
  }

  /** Auxiliary On-Us */
  get auxiliaryOnUs(): Field | undefined {
    return this.document.fields.find((f: Field) => f.type.name === 'AuxiliaryOnUs');
  }

  /** Transit number */
  get transitNumber(): Field {
    return this.document.fields.find((f: Field) => f.type.name === 'TransitNumber') as Field;
  }
}

/** A check format commonly used in the United Arab Emirates */
export class UAECheck extends Check {
  constructor(document: GenericDocument) {
    super(document);
  }

  requiredDocumentType(): string {
    return UAECheckDocumentType;
  }

  /** Account number */
  get accountNumber(): Field {
    return this.document.fields.find((f: Field) => f.type.name === 'AccountNumber') as Field;
  }

  /** Cheque number */
  get chequeNumber(): Field {
    return this.document.fields.find((f: Field) => f.type.name === 'ChequeNumber') as Field;
  }

  /** Routing number */
  get routingNumber(): Field {
    return this.document.fields.find((f: Field) => f.type.name === 'RoutingNumber') as Field;
  }
}

/** A check format commonly used in France */
export class FRACheck extends Check {
  constructor(document: GenericDocument) {
    super(document);
  }

  requiredDocumentType(): string {
    return FRACheckDocumentType;
  }

  /** Account number */
  get accountNumber(): Field {
    return this.document.fields.find((f: Field) => f.type.name === 'AccountNumber') as Field;
  }

  /** Cheque number */
  get chequeNumber(): Field {
    return this.document.fields.find((f: Field) => f.type.name === 'ChequeNumber') as Field;
  }

  /** Routing number */
  get routingNumber(): Field {
    return this.document.fields.find((f: Field) => f.type.name === 'RoutingNumber') as Field;
  }
}

/** A check format commonly used in Israel */
export class ISRCheck extends Check {
  constructor(document: GenericDocument) {
    super(document);
  }

  requiredDocumentType(): string {
    return ISRCheckDocumentType;
  }

  /** Account number */
  get accountNumber(): Field {
    return this.document.fields.find((f: Field) => f.type.name === 'AccountNumber') as Field;
  }

  /** Bank number */
  get bankNumber(): Field {
    return this.document.fields.find((f: Field) => f.type.name === 'BankNumber') as Field;
  }

  /** Branch number */
  get branchNumber(): Field {
    return this.document.fields.find((f: Field) => f.type.name === 'BranchNumber') as Field;
  }

  /** Cheque number */
  get chequeNumber(): Field {
    return this.document.fields.find((f: Field) => f.type.name === 'ChequeNumber') as Field;
  }
}

/** A check format commonly used in Kuwait */
export class KWTCheck extends Check {
  constructor(document: GenericDocument) {
    super(document);
  }

  requiredDocumentType(): string {
    return KWTCheckDocumentType;
  }

  /** Account number */
  get accountNumber(): Field {
    return this.document.fields.find((f: Field) => f.type.name === 'AccountNumber') as Field;
  }

  /** Cheque number */
  get chequeNumber(): Field {
    return this.document.fields.find((f: Field) => f.type.name === 'ChequeNumber') as Field;
  }

  /** Sort code */
  get sortCode(): Field {
    return this.document.fields.find((f: Field) => f.type.name === 'SortCode') as Field;
  }
}

/** A check compatible with the Australian Paper Clearing System cheque standard */
export class AUSCheck extends Check {
  constructor(document: GenericDocument) {
    super(document);
  }

  requiredDocumentType(): string {
    return AUSCheckDocumentType;
  }

  /** Account number */
  get accountNumber(): Field {
    return this.document.fields.find((f: Field) => f.type.name === 'AccountNumber') as Field;
  }

  /** Auxiliary domestic */
  get auxDomestic(): Field | undefined {
    return this.document.fields.find((f: Field) => f.type.name === 'AuxDomestic');
  }

  /** BSB */
  get bsb(): Field {
    return this.document.fields.find((f: Field) => f.type.name === 'BSB') as Field;
  }

  /** Extra auxiliary domestic */
  get extraAuxDomestic(): Field | undefined {
    return this.document.fields.find((f: Field) => f.type.name === 'ExtraAuxDomestic');
  }

  /** Transaction code */
  get transactionCode(): Field {
    return this.document.fields.find((f: Field) => f.type.name === 'TransactionCode') as Field;
  }
}

/** A check compatible with the CTS-2010 standard issued by the Reserve Bank of India in 2012 */
export class INDCheck extends Check {
  constructor(document: GenericDocument) {
    super(document);
  }

  requiredDocumentType(): string {
    return INDCheckDocumentType;
  }

  /** Account number */
  get accountNumber(): Field {
    return this.document.fields.find((f: Field) => f.type.name === 'AccountNumber') as Field;
  }

  /** Serial number */
  get serialNumber(): Field {
    return this.document.fields.find((f: Field) => f.type.name === 'SerialNumber') as Field;
  }

  /** Sort number */
  get sortNumber(): Field | undefined {
    return this.document.fields.find((f: Field) => f.type.name === 'SortNumber');
  }

  /** Transaction code */
  get transactionCode(): Field {
    return this.document.fields.find((f: Field) => f.type.name === 'TransactionCode') as Field;
  }
}

/** A check format commonly used in Canada */
export class CANCheck extends Check {
  constructor(document: GenericDocument) {
    super(document);
  }

  requiredDocumentType(): string {
    return CANCheckDocumentType;
  }

  /** Account number */
  get accountNumber(): Field {
    return this.document.fields.find((f: Field) => f.type.name === 'AccountNumber') as Field;
  }

  /** Bank number */
  get bankNumber(): Field {
    return this.document.fields.find((f: Field) => f.type.name === 'BankNumber') as Field;
  }

  /** Cheque number */
  get chequeNumber(): Field {
    return this.document.fields.find((f: Field) => f.type.name === 'ChequeNumber') as Field;
  }

  /** Designation number */
  get designationNumber(): Field | undefined {
    return this.document.fields.find((f: Field) => f.type.name === 'DesignationNumber');
  }

  /** Transaction code */
  get transactionCode(): Field | undefined {
    return this.document.fields.find((f: Field) => f.type.name === 'TransactionCode');
  }

  /** Transit number */
  get transitNumber(): Field {
    return this.document.fields.find((f: Field) => f.type.name === 'TransitNumber') as Field;
  }
}

/** A check that doesn't conform to any supported standard */
export class UnknownCheck extends Check {
  constructor(document: GenericDocument) {
    super(document);
  }

  requiredDocumentType(): string {
    return UnknownCheckDocumentType;
  }
}
