import { Amounts } from "../amounts.js";
import { AccountProperties, KycRule, LimitOperationType } from "../types-taler-exchange.js";
import { TalerAmlProperties } from "../taler-account-properties.js";
import { isOneOf } from "./properties.js";

/**
 * List of events triggered by TOPS
 */
export enum TOPS_AmlEventsName {
  INCR_ACCOUNT_OPEN = "INCR_ACCOUNT_OPEN",
  DECR_ACCOUNT_OPEN = "DECR_ACCOUNT_OPEN",
  INCR_HIGH_RISK_CUSTOMER = "INCR_HIGH_RISK_CUSTOMER",
  DECR_HIGH_RISK_CUSTOMER = "DECR_HIGH_RISK_CUSTOMER",
  INCR_HIGH_RISK_COUNTRY = "INCR_HIGH_RISK_COUNTRY",
  DECR_HIGH_RISK_COUNTRY = "DECR_HIGH_RISK_COUNTRY",
  INCR_PEP = "INCR_PEP",
  DECR_PEP = "DECR_PEP",
  INCR_PEP_FOREIGN = "INCR_PEP_FOREIGN",
  DECR_PEP_FOREIGN = "DECR_PEP_FOREIGN",
  INCR_PEP_DOMESTIC = "INCR_PEP_DOMESTIC",
  DECR_PEP_DOMESTIC = "DECR_PEP_DOMESTIC",
  INCR_PEP_INTERNATIONAL_ORGANIZATION = "INCR_PEP_INTERNATIONAL_ORGANIZATION",
  DECR_PEP_INTERNATIONAL_ORGANIZATION = "DECR_PEP_INTERNATIONAL_ORGANIZATION",
  MROS_REPORTED_SUSPICION_SIMPLE = "MROS_REPORTED_SUSPICION_SIMPLE",
  MROS_REPORTED_SUSPICION_SUBSTANTIATED = "MROS_REPORTED_SUSPICION_SUBSTANTIATED",
  INCR_INVESTIGATION_CONCLUDED = "INCR_INVESTIGATION_CONCLUDED",
  DECR_INVESTIGATION_CONCLUDED = "DECR_INVESTIGATION_CONCLUDED",
}

/**
 * List of events triggered by GLS
 */
export enum GLS_AmlEventsName {
  ACCOUNT_OPENED = "ACCOUNT_OPENED",
  ACCOUNT_CLOSED = "ACCOUNT_CLOSED",
}

enum KnownForms {
  vqf_902_1_customer,
  vqf_902_1_officer,
  vqf_902_4,
  vqf_902_5,
  vqf_902_9_customer,
  vqf_902_9_officer,
  vqf_902_11_customer,
  vqf_902_11_officer,
  vqf_902_12,
  vqf_902_13,
  vqf_902_14,
  vqf_902_15,
}

export type EventMapInfo<T> = {
  [name in keyof T]: {
    /**
     * Based on the current properties and next properties,
     * the current account limits and new attributes of the account
     * calculate if the event should be triggered.
     *
     * return false if there is no enough information to decide.
     *
     * @param prevLimits current active decision limits, undefined if this account has no active decision yet
     * @param nextLimits limits of the decision to be made, undefined if not yet decided
     * @param prevState current active decision properties, undefined if this account has no active decision yet
     * @param nextState new properties of the account defined by the decision, undefined if not yet decided
     * @param newAttributes new information added by this decision
     * @returns
     */
    shouldBeTriggered: (
      formId: string,
      prevLimits: KycRule[] | undefined,
      nextLimits: KycRule[] | undefined,
      prevState: AccountProperties | undefined,
      nextState: AccountProperties | undefined,
      newAttributes: Record<string, unknown>,
    ) => boolean;
  };
};

function isAllowToMakeDeposits(limits: KycRule[]) {
  const depositLimits = limits.filter(
    (r) => r.operation_type === LimitOperationType.deposit,
  );
  // no deposit limits
  if (!depositLimits.length) return true;
  const zero = depositLimits.find((d) => Amounts.isZero(d.threshold));
  // there is a rule that prohibit deposit
  if (zero) return false;
  // the cusomter can at least make some deposits
  return true;
}

function propBecameTrue(
  prevState: AccountProperties | undefined,
  nextState: AccountProperties,
  prop: string,
): boolean {
  const wasFalse = prevState === undefined || !prevState[prop];
  const isTrue = !!nextState[prop];
  return wasFalse && isTrue;
}

function propBecameFalse(
  prevState: AccountProperties | undefined,
  nextState: AccountProperties,
  prop: string,
): boolean {
  const wasTrue = prevState !== undefined && !!prevState[prop];
  const isFalse = !nextState[prop];
  return wasTrue && isFalse;
}

function isAnyKindOfPep(state: AccountProperties): boolean {
  return (
    !!state[TalerAmlProperties.PEP_INTERNATIONAL_ORGANIZATION] ||
    !!state[TalerAmlProperties.PEP_DOMESTIC] ||
    !!state[TalerAmlProperties.PEP_FOREIGN]
  );
}

/**
 * Calculate if an event should be triggered for TOPS decisions
 */
export const EventsDerivation_TOPS: EventMapInfo<typeof TOPS_AmlEventsName> = {
  INCR_ACCOUNT_OPEN: {
    shouldBeTriggered(formId, pL, nL, prevState, nextState, attr) {
      if (nextState === undefined) {
        return false;
      }
      if (
        isOneOf(
          formId,
          KnownForms.vqf_902_1_customer,
          KnownForms.vqf_902_1_officer,
        ) &&
        propBecameTrue(prevState, nextState, TalerAmlProperties.ACCOUNT_OPEN)
      ) {
        return true; // # event-rule 1
      }

      return false;
    },
  },
  DECR_ACCOUNT_OPEN: {
    shouldBeTriggered(formId, pL, nL, prevState, nextState, attr) {
      return false;
    },
  },
  INCR_HIGH_RISK_CUSTOMER: {
    shouldBeTriggered(formId, pL, nL, prevState, nextState, attr) {
      if (nextState === undefined) {
        return false;
      }
      if (
        isOneOf(
          formId,
          KnownForms.vqf_902_1_customer,
          KnownForms.vqf_902_1_officer,
        ) &&
        propBecameTrue(prevState, nextState, TalerAmlProperties.ACCOUNT_OPEN) &&
        !!nextState[TalerAmlProperties.HIGH_RISK_CUSTOMER]
      ) {
        return true; // # event-rule 6
      }
      // only accounts with history after this
      if (prevState === undefined) {
        return false;
      }
      if (
        isOneOf(formId, KnownForms.vqf_902_4) &&
        !!prevState[TalerAmlProperties.ACCOUNT_OPEN] &&
        propBecameTrue(
          prevState,
          nextState,
          TalerAmlProperties.HIGH_RISK_CUSTOMER,
        )
      ) {
        return true; // # event-rule 18
      }
      return false;
    },
  },
  DECR_HIGH_RISK_CUSTOMER: {
    shouldBeTriggered(formId, pL, nL, prevState, nextState, attr) {
      if (nextState === undefined) {
        return false;
      }
      // only accounts with history after this
      if (prevState === undefined) {
        return false;
      }
      if (
        isOneOf(formId, KnownForms.vqf_902_4) &&
        !!prevState[TalerAmlProperties.ACCOUNT_OPEN] &&
        propBecameFalse(
          prevState,
          nextState,
          TalerAmlProperties.HIGH_RISK_CUSTOMER,
        )
      ) {
        return true; // # event-rule 19
      }
      return false;
    },
  },
  INCR_HIGH_RISK_COUNTRY: {
    shouldBeTriggered(formId, pL, nL, prevState, nextState, attr) {
      if (nextState === undefined) {
        return false;
      }
      if (
        isOneOf(
          formId,
          KnownForms.vqf_902_1_customer,
          KnownForms.vqf_902_1_officer,
        ) &&
        propBecameTrue(prevState, nextState, TalerAmlProperties.ACCOUNT_OPEN) &&
        !!nextState[TalerAmlProperties.HIGH_RISK_COUNTRY]
      ) {
        return true; // # event-rule 7
      }
      // only accounts with history after this
      if (prevState === undefined) {
        return false;
      }
      if (
        isOneOf(formId, KnownForms.vqf_902_4) &&
        !!prevState[TalerAmlProperties.ACCOUNT_OPEN] &&
        propBecameTrue(
          prevState,
          nextState,
          TalerAmlProperties.HIGH_RISK_CUSTOMER,
        )
      ) {
        return true; // # event-rule 16
      }
      return false;
    },
  },
  DECR_HIGH_RISK_COUNTRY: {
    shouldBeTriggered(formId, pL, nL, prevState, nextState, attr) {
      if (nextState === undefined) {
        return false;
      }
      // only accounts with history after this
      if (prevState === undefined) {
        return false;
      }
      if (
        isOneOf(formId, KnownForms.vqf_902_4) &&
        !!prevState[TalerAmlProperties.ACCOUNT_OPEN] &&
        propBecameFalse(
          prevState,
          nextState,
          TalerAmlProperties.HIGH_RISK_CUSTOMER,
        )
      ) {
        return true; // # event-rule 17
      }
      return false;
    },
  },
  INCR_PEP: {
    shouldBeTriggered(formId, pL, nL, prevState, nextState, attr) {
      if (nextState === undefined) {
        return false;
      }
      const isPep = isAnyKindOfPep(nextState);
      if (
        isOneOf(
          formId,
          KnownForms.vqf_902_1_customer,
          KnownForms.vqf_902_1_officer,
        ) &&
        propBecameTrue(prevState, nextState, TalerAmlProperties.ACCOUNT_OPEN) &&
        isPep
      ) {
        return true; // # event-rule 2
      }
      // only accounts with history after this
      if (prevState === undefined) {
        return false;
      }
      const wasPep = isAnyKindOfPep(prevState);
      if (
        isOneOf(formId, KnownForms.vqf_902_4) &&
        !!prevState[TalerAmlProperties.ACCOUNT_OPEN] &&
        !wasPep &&
        isPep
      ) {
        return true; // # event-rule 15
      }
      return false;
    },
  },
  DECR_PEP: {
    shouldBeTriggered(formId, pL, nL, prevState, nextState, attr) {
      if (nextState === undefined) {
        return false;
      }
      // only accounts with history after this
      if (prevState === undefined) {
        return false;
      }
      const wasPep = isAnyKindOfPep(prevState);
      const isPep = isAnyKindOfPep(nextState);
      if (
        isOneOf(formId, KnownForms.vqf_902_4) &&
        !!prevState[TalerAmlProperties.ACCOUNT_OPEN] &&
        wasPep &&
        !isPep
      ) {
        return true; // # event-rule 14
      }
      return false;
    },
  },
  INCR_PEP_FOREIGN: {
    shouldBeTriggered(formId, pL, nL, prevState, nextState, attr) {
      if (nextState === undefined) {
        return false;
      }
      if (
        isOneOf(
          formId,
          KnownForms.vqf_902_1_customer,
          KnownForms.vqf_902_1_officer,
        ) &&
        propBecameTrue(prevState, nextState, TalerAmlProperties.ACCOUNT_OPEN) &&
        !!nextState[TalerAmlProperties.PEP_FOREIGN]
      ) {
        return true; // # event-rule 3
      }
      // only accounts with history after this
      if (prevState === undefined) {
        return false;
      }
      if (
        isOneOf(formId, KnownForms.vqf_902_4) &&
        !!prevState[TalerAmlProperties.ACCOUNT_OPEN] &&
        propBecameTrue(prevState, nextState, TalerAmlProperties.PEP_FOREIGN)
      ) {
        return true; // # event-rule 8
      }
      return false;
    },
  },
  DECR_PEP_FOREIGN: {
    shouldBeTriggered(formId, pL, nL, prevState, nextState, attr) {
      if (nextState === undefined) {
        return false;
      }
      // only accounts with history after this
      if (prevState === undefined) {
        return false;
      }
      if (
        isOneOf(formId, KnownForms.vqf_902_4) &&
        !!prevState[TalerAmlProperties.ACCOUNT_OPEN] &&
        propBecameFalse(prevState, nextState, TalerAmlProperties.PEP_FOREIGN)
      ) {
        return true; // # event-rule 11
      }
      return false;
    },
  },
  INCR_PEP_DOMESTIC: {
    shouldBeTriggered(formId, pL, nL, prevState, nextState, attr) {
      if (nextState === undefined) {
        return false;
      }
      if (
        isOneOf(
          formId,
          KnownForms.vqf_902_1_customer,
          KnownForms.vqf_902_1_officer,
        ) &&
        propBecameTrue(prevState, nextState, TalerAmlProperties.ACCOUNT_OPEN) &&
        !!nextState[TalerAmlProperties.PEP_DOMESTIC]
      ) {
        return true; // # event-rule 4
      }
      // only accounts with history after this
      if (prevState === undefined) {
        return false;
      }
      if (
        isOneOf(formId, KnownForms.vqf_902_4) &&
        !!prevState[TalerAmlProperties.ACCOUNT_OPEN] &&
        propBecameTrue(prevState, nextState, TalerAmlProperties.PEP_DOMESTIC)
      ) {
        return true; // # event-rule 10
      }
      return false;
    },
  },
  DECR_PEP_DOMESTIC: {
    shouldBeTriggered(formId, pL, nL, prevState, nextState, attr) {
      if (nextState === undefined) {
        return false;
      }
      // only accounts with history after this
      if (prevState === undefined) {
        return false;
      }
      if (
        isOneOf(formId, KnownForms.vqf_902_4) &&
        !!prevState[TalerAmlProperties.ACCOUNT_OPEN] &&
        propBecameFalse(prevState, nextState, TalerAmlProperties.PEP_DOMESTIC)
      ) {
        return true; // # event-rule 13
      }
      return false;
    },
  },
  INCR_PEP_INTERNATIONAL_ORGANIZATION: {
    shouldBeTriggered(formId, pL, nL, prevState, nextState, attr) {
      if (nextState === undefined) {
        return false;
      }
      if (
        isOneOf(
          formId,
          KnownForms.vqf_902_1_customer,
          KnownForms.vqf_902_1_officer,
        ) &&
        propBecameTrue(prevState, nextState, TalerAmlProperties.ACCOUNT_OPEN) &&
        !!nextState[TalerAmlProperties.PEP_INTERNATIONAL_ORGANIZATION]
      ) {
        return true; // # event-rule 5
      }
      // only accounts with history after this
      if (prevState === undefined) {
        return false;
      }
      if (
        isOneOf(formId, KnownForms.vqf_902_4) &&
        !!prevState[TalerAmlProperties.ACCOUNT_OPEN] &&
        propBecameTrue(
          prevState,
          nextState,
          TalerAmlProperties.PEP_INTERNATIONAL_ORGANIZATION,
        )
      ) {
        return true; // # event-rule 9
      }
      return false;
    },
  },
  DECR_PEP_INTERNATIONAL_ORGANIZATION: {
    shouldBeTriggered(formId, pL, nL, prevState, nextState, attr) {
      if (nextState === undefined) {
        return false;
      }
      // only accounts with history after this
      if (prevState === undefined) {
        return false;
      }
      if (
        isOneOf(formId, KnownForms.vqf_902_4) &&
        !!prevState[TalerAmlProperties.ACCOUNT_OPEN] &&
        propBecameFalse(
          prevState,
          nextState,
          TalerAmlProperties.PEP_INTERNATIONAL_ORGANIZATION,
        )
      ) {
        return true; // # event-rule 12
      }
      return false;
    },
  },
  MROS_REPORTED_SUSPICION_SIMPLE: {
    shouldBeTriggered(formId, pL, nL, prevState, nextState, attr) {
      if (prevState === undefined || nextState === undefined) {
        return false;
      }
      if (
        prevState[TalerAmlProperties.INVESTIGATION_STATE] === "NONE" ||
        prevState[TalerAmlProperties.INVESTIGATION_STATE] ===
          "INVESTIGATION_PENDING" ||
        !prevState[TalerAmlProperties.INVESTIGATION_STATE]
      ) {
        if (
          nextState[TalerAmlProperties.INVESTIGATION_STATE] ===
          "REPORTED_SUSPICION_SIMPLE"
        ) {
          return true; // # event-rule 22
        }
      }
      return false;
    },
  },
  MROS_REPORTED_SUSPICION_SUBSTANTIATED: {
    shouldBeTriggered(formId, pL, nL, prevState, nextState, attr) {
      if (prevState === undefined || nextState === undefined) {
        return false;
      }
      if (
        prevState[TalerAmlProperties.INVESTIGATION_STATE] === "NONE" ||
        prevState[TalerAmlProperties.INVESTIGATION_STATE] ===
          "INVESTIGATION_PENDING" ||
        !prevState[TalerAmlProperties.INVESTIGATION_STATE]
      ) {
        if (
          nextState[TalerAmlProperties.INVESTIGATION_STATE] ===
          "REPORTED_SUSPICION_SUBSTANTIATED"
        ) {
          return true; // # event-rule 21
        }
      }
      return false;
    },
  },
  INCR_INVESTIGATION_CONCLUDED: {
    shouldBeTriggered(formId, pL, nL, prevState, nextState, attr) {
      if (prevState === undefined || nextState === undefined) {
        return false;
      }
      if (
        prevState[TalerAmlProperties.INVESTIGATION_STATE] === "NONE" ||
        prevState[TalerAmlProperties.INVESTIGATION_STATE] ===
          "INVESTIGATION_PENDING" ||
        !prevState[TalerAmlProperties.INVESTIGATION_STATE]
      ) {
        if (
          nextState[TalerAmlProperties.INVESTIGATION_STATE] ===
            "REPORTED_SUSPICION_SIMPLE" ||
          nextState[TalerAmlProperties.INVESTIGATION_STATE] ===
            "REPORTED_SUSPICION_SUBSTANTIATED" ||
          nextState[TalerAmlProperties.INVESTIGATION_STATE] ===
            "INVESTIGATION_COMPLETED_WITHOUT_SUSPICION"
        ) {
          return true; // # event-rule 20
        }
      }
      return false;
    },
  },
  DECR_INVESTIGATION_CONCLUDED: {
    shouldBeTriggered(formId, pL, nL, prevState, nextState, attr) {
      return false;
    },
  },
};

export const GLS_AML_EVENTS: EventMapInfo<typeof GLS_AmlEventsName> = {
  ACCOUNT_OPENED: {
    shouldBeTriggered(formId, pL, nL, prevState, nextState, attr) {
      return false;
    },
  },
  ACCOUNT_CLOSED: {
    shouldBeTriggered(formId, pL, nL, prevState, nextState, attr) {
      return false;
    },
  },
};
