// SPDX-License-Identifier: Apache-2.0
import { EmptyValue } from "../../../core/error/EmptyValue";
import { InvalidLength } from "./error/InvalidLength";
import { InvalidRange } from "./error/InvalidRange";
import { InvalidFormatHedera as InvalidIdFormatHedera } from "../../../domain/context/shared/error/InvalidFormatHedera";
import { InvalidType } from "./error/InvalidType";
import PublicKey from "../../../domain/context/account/PublicKey";
import CheckStrings from "../../../core/checks/strings/CheckStrings";
import CheckNums from "../../../core/checks/numbers/CheckNums";
import { AccountIdNotValid } from "../../../domain/context/account/error/AccountIdNotValid";
import BigDecimal from "../../../domain/context/shared/BigDecimal";
import Account from "../../../domain/context/account/Account";
import ContractId from "../../../domain/context/contract/ContractId";
import InvalidDecimalRange from "../../../domain/context/security/error/values/InvalidDecimalRange";
import { SecurityRole } from "../../../domain/context/security/SecurityRole";
import { InvalidRole } from "../../../domain/context/security/error/values/InvalidRole";
import { EVM_ZERO_ADDRESS } from "../../../core/Constants";
import { InvalidEvmAddress } from "../../../domain/context/contract/error/InvalidEvmAddress";
import { InvalidFormatHederaIdOrEvmAddress } from "../../../domain/context/shared/error/InvalidFormatHederaIdOrEvmAddress";
import { InvalidBytes32 } from "./error/InvalidBytes32";
import { InvalidBytes3 } from "./error/InvalidBytes3";
import { HEDERA_FORMAT_ID_REGEX } from "../../../domain/context/shared/HederaId";
import { InvalidBytes } from "./error/InvalidBytes";
import { InvalidBase64 } from "./error/InvalidBase64";
import { InvalidValue } from "./error/InvalidValue";
export default class FormatValidation {
    static checkPublicKey = () => {
        return (val) => {
            const key = val;
            return PublicKey.validate(key);
        };
    };
    static checkContractId = () => {
        return (val) => {
            return ContractId.validate(val);
        };
    };
    static checkString = ({ max = Number.MAX_VALUE, min = 0, emptyCheck = true }) => {
        return (val) => {
            const err = [];
            if (typeof val !== "string") {
                err.push(new InvalidType(val));
            }
            else {
                if (emptyCheck && !CheckStrings.isNotEmpty(val)) {
                    err.push(new EmptyValue(val));
                }
                else if (!CheckStrings.isLengthBetween(val, min, max)) {
                    err.push(new InvalidLength(val, min, max));
                }
            }
            return err;
        };
    };
    static checkNumber = ({ max, min } = {}) => {
        return (val) => {
            const err = [];
            const iMax = max || max === 0;
            const iMin = min || min === 0;
            const isBigDecimal = CheckNums.isBigDecimal(val);
            if (typeof val !== "number" && !isBigDecimal) {
                err.push(new InvalidType(val));
            }
            else {
                let v = val;
                if (typeof v !== "number" && !(v instanceof BigDecimal))
                    v = BigDecimal.fromString(v);
                if (iMin && !iMax) {
                    if (CheckNums.isLessThan(v, min)) {
                        err.push(new InvalidRange(v, min));
                    }
                }
                else if (!iMin && iMax) {
                    if (CheckNums.isGreaterThan(v, max)) {
                        err.push(new InvalidRange(v, undefined, max));
                    }
                }
                else if (iMin && iMax) {
                    if (!CheckNums.isWithinRange(v, min, max)) {
                        err.push(new InvalidRange(v, min, max));
                    }
                }
            }
            return err;
        };
    };
    static checkArrayNumber = ({ max, min } = {}) => {
        return (val) => {
            const err = [];
            const check = this.checkNumber({ max, min });
            for (const v of val) {
                const e = check(v);
                if (e.length > 0) {
                    err.push(...e);
                }
            }
            return err;
        };
    };
    static checkRole = () => {
        return (val) => {
            const err = [];
            const roles = Object.values(SecurityRole);
            if (!roles.includes(val)) {
                err.push(new InvalidRole(val));
            }
            return err;
        };
    };
    static checkAccount = () => {
        return (val) => {
            const { accountId, publicKey, evmAddress } = val;
            if (publicKey) {
                new Account({
                    id: accountId,
                    publicKey: new PublicKey(publicKey),
                    evmAddress,
                });
            }
            else {
                new Account({
                    id: accountId,
                    evmAddress,
                });
            }
        };
    };
    static checkHederaIdFormat = (zeroIsValid = false) => {
        return (val) => {
            // Account Id defined in hip-15 : https://hips.hedera.com/hip/hip-15
            const err = [];
            if (!HEDERA_FORMAT_ID_REGEX.exec(val)) {
                err.push(new InvalidIdFormatHedera(val));
            }
            else if (!zeroIsValid && val === "0.0.0") {
                err.push(new AccountIdNotValid(val));
            }
            return err;
        };
    };
    static checkEvmAddressFormat = (zeroIsValid = false) => {
        return (val) => {
            const evmAddressRegEx = /^0x[a-fA-F0-9]{40}$/;
            const err = [];
            if (!evmAddressRegEx.exec(val)) {
                err.push(new InvalidEvmAddress(val));
            }
            else if (!zeroIsValid && val === EVM_ZERO_ADDRESS) {
                err.push(new AccountIdNotValid(val));
            }
            return err;
        };
    };
    static checkHederaIdFormatOrEvmAddress = (zeroIsValid = false) => {
        return (val) => {
            // Account Id defined in hip-15 : https://hips.hedera.com/hip/hip-15
            const evmAddressRegEx = /^0x[a-fA-F0-9]{40}$/;
            const err = [];
            if (!HEDERA_FORMAT_ID_REGEX.exec(val) && !evmAddressRegEx.exec(val)) {
                err.push(new InvalidFormatHederaIdOrEvmAddress(val));
            }
            else if (!zeroIsValid && (val === "0.0.0" || val === EVM_ZERO_ADDRESS)) {
                err.push(new AccountIdNotValid(val));
            }
            return err;
        };
    };
    static checkAmount = (zeroIsValid = false, decimals = 18) => {
        return (val) => {
            const err = [];
            const isBigDecimal = CheckNums.isBigDecimal(val);
            if (!isBigDecimal) {
                err.push(new InvalidType(val));
                return err;
            }
            const valueDecimals = BigDecimal.getDecimalsFromString(val);
            const zero = BigDecimal.fromString("0", valueDecimals);
            const value = BigDecimal.fromString(val);
            if (zeroIsValid && value.isLowerThan(zero))
                err.push(new InvalidRange(val, "0", undefined));
            else if (!zeroIsValid && value.isLowerOrEqualThan(zero))
                err.push(new InvalidRange(val, "0", undefined));
            if (valueDecimals > decimals) {
                err.push(new InvalidDecimalRange(val, 0, decimals));
            }
            return err;
        };
    };
    static checkBytes32Format = () => {
        return (val) => {
            const bytes32RegEx = /^0x[a-fA-F0-9]{64}$/;
            const err = [];
            if (!bytes32RegEx.exec(val)) {
                err.push(new InvalidBytes32(val));
            }
            return err;
        };
    };
    static checkBytes3Format = () => {
        return (val) => {
            const bytes3RegEx = /^0x[a-fA-F0-9]{6}$/;
            const err = [];
            if (!bytes3RegEx.exec(val)) {
                err.push(new InvalidBytes3(val));
            }
            return err;
        };
    };
    static checkBytesFormat = () => {
        return (val) => {
            const bytesRegEx = /^0x([a-fA-F0-9][a-fA-F0-9])*$/;
            const err = [];
            if (!bytesRegEx.exec(val)) {
                err.push(new InvalidBytes(val));
            }
            return err;
        };
    };
    static checkBase64Format = () => {
        return (val) => {
            const base64RegEx = /^[a-zA-Z0-9+/]*={0,2}$/;
            const err = [];
            if (!base64RegEx.exec(val)) {
                err.push(new InvalidBase64(val));
            }
            return err;
        };
    };
    static checkHederaIdOrEvmAddressArray(values, fieldName, allowEmpty = false) {
        if (values.length === 0) {
            return allowEmpty ? [] : [new InvalidValue(`The list of ${fieldName} cannot be empty`)];
        }
        const errors = [];
        const seenValues = new Set();
        values.forEach((value) => {
            const formatErrors = FormatValidation.checkHederaIdFormatOrEvmAddress()(value);
            errors.push(...formatErrors);
            if (seenValues.has(value)) {
                errors.push(new InvalidValue(`${fieldName} ${value} is duplicated`));
            }
            seenValues.add(value);
        });
        return errors;
    }
    static checkBoolean = () => {
        return (val) => {
            const err = [];
            if (typeof val !== "boolean") {
                err.push(new InvalidType(val));
            }
            return err;
        };
    };
}
