import CryptoJS from "crypto-js";
export class SpamError extends Error {}
const DEFAULT_NAME_FIELD_NAME = "name__confirm";
const DEFAULT_VALID_FROM_FIELD_NAME = "from__confirm";
export class Honeypot {
  config;
  generatedEncryptionSeed = this.randomValue();
  constructor(config = {}) {
    this.config = config;
  }
  getInputProps({ validFromTimestamp = Date.now() } = {}) {
    return {
      nameFieldName: this.nameFieldName,
      validFromFieldName: this.validFromFieldName,
      encryptedValidFrom: this.encrypt(validFromTimestamp.toString()),
    };
  }
  check(formData) {
    let nameFieldName = this.config.nameFieldName ?? DEFAULT_NAME_FIELD_NAME;
    if (this.config.randomizeNameFieldName) {
      let actualName = this.getRandomizedNameFieldName(nameFieldName, formData);
      if (actualName) nameFieldName = actualName;
    }
    if (!this.shouldCheckHoneypot(formData, nameFieldName)) return;
    if (!formData.has(nameFieldName)) {
      throw new SpamError("Missing honeypot input");
    }
    let honeypotValue = formData.get(nameFieldName);
    if (honeypotValue !== "") throw new SpamError("Honeypot input not empty");
    if (!this.validFromFieldName) return;
    let validFrom = formData.get(this.validFromFieldName);
    if (!validFrom) throw new SpamError("Missing honeypot valid from input");
    let time = this.decrypt(validFrom);
    if (!time) throw new SpamError("Invalid honeypot valid from input");
    if (!this.isValidTimeStamp(Number(time))) {
      throw new SpamError("Invalid honeypot valid from input");
    }
    if (this.isFuture(Number(time))) {
      throw new SpamError("Honeypot valid from is in future");
    }
  }
  get nameFieldName() {
    let fieldName = this.config.nameFieldName ?? DEFAULT_NAME_FIELD_NAME;
    if (!this.config.randomizeNameFieldName) return fieldName;
    return `${fieldName}_${this.randomValue()}`;
  }
  get validFromFieldName() {
    if (this.config.validFromFieldName === undefined) {
      return DEFAULT_VALID_FROM_FIELD_NAME;
    }
    return this.config.validFromFieldName;
  }
  get encryptionSeed() {
    return this.config.encryptionSeed ?? this.generatedEncryptionSeed;
  }
  getRandomizedNameFieldName(nameFieldName, formData) {
    for (let key of formData.keys()) {
      if (!key.startsWith(nameFieldName)) continue;
      return key;
    }
  }
  shouldCheckHoneypot(formData, nameFieldName) {
    return (
      formData.has(nameFieldName) ||
      Boolean(this.validFromFieldName && formData.has(this.validFromFieldName))
    );
  }
  randomValue() {
    return CryptoJS.lib.WordArray.random(128 / 8).toString();
  }
  encrypt(value) {
    return CryptoJS.AES.encrypt(value, this.encryptionSeed).toString();
  }
  decrypt(value) {
    return CryptoJS.AES.decrypt(value, this.encryptionSeed).toString(
      CryptoJS.enc.Utf8
    );
  }
  isFuture(timestamp) {
    return timestamp > Date.now();
  }
  isValidTimeStamp(timestampp) {
    if (Number.isNaN(timestampp)) return false;
    if (timestampp <= 0) return false;
    if (timestampp >= Number.MAX_SAFE_INTEGER) return false;
    return true;
  }
}
