import { numberList, specialList, lowerWordList, upperWordList } from './constants';

type map = <U, T>(opstion: {
    array: U[];
    range: number;
    matchList: T[];
    tokenMap: (updateItem: T, token: U, index: number) => T;
    breakKey?: string;
    arrayMap?: (item: U, index: number) => void;
}) => T[];

/**
 * match array and set
 */
export const setArrayMatch: map = ({ array, range, matchList, breakKey, tokenMap, arrayMap }) => {
    const tokenLen = array.length;
    for (let tokenIndex = tokenLen - 1; tokenIndex >= 0; tokenIndex--) {
        const arrayToken = array[tokenIndex];
        arrayMap && arrayMap(arrayToken, tokenIndex);
        for (let findIndex = range - 1; findIndex >= 0; findIndex--) {
            matchList = matchList.map(item => tokenMap(item, arrayToken, findIndex));
        }
        if (breakKey && !matchList.map(e => (e as any)[breakKey]).includes(false)) {
            break;
        }
    }
    return matchList;
};

export const passwordBreakKey = 'isMatch';
export type IpasswordRule = {
    list: string[];
    isMatch: boolean;
    name: string;
};
export const defaultPasswordRuleList = [
    { name: 'special', list: specialList },
    { name: 'num', list: numberList },
    { name: 'lowerWord', list: lowerWordList },
    { name: 'upperWord', list: upperWordList },
];

type PickValue<T, K extends keyof T> = T[K];

export const getRuleMatchResult: (
    customRule: IpasswordRule[],
    inputValue: string,
    disableDefaultRule?: boolean,
    breakKey?: string,
) => {
    list: IpasswordRule[];
    map: Map<PickValue<IpasswordRule, 'name'>, boolean>;
    matchCount: number;
    invaildWord: boolean;
} = (customRule, inputValue, disableDefaultRule = true, breakKey) => {
    let ruleList = [...(disableDefaultRule ? defaultPasswordRuleList : []), ...customRule].map(item => ({ ...item, [passwordBreakKey]: false }));
    const range = Math.max(...ruleList.map(ruleItem => ruleItem.list.length));
    let matchCount = 0;
    ruleList = setArrayMatch<string, IpasswordRule>({
        array: inputValue.split(''),
        range,
        matchList: ruleList,
        // not breakKey  full match
        breakKey: breakKey === void 0 ? passwordBreakKey : breakKey,
        tokenMap: (ruleItem, inputToken, findIndex) => {
            const match = ruleItem?.list[findIndex] === inputToken;
            if (match) {
                matchCount++;
                return { ...ruleItem, isMatch: true };
            }
            return ruleItem;
        },
    });
    return {
        list: ruleList,
        map: new Map(ruleList.map(e => [e.name, e[passwordBreakKey]])),
        matchCount,
        // 想要获取这个值，必须breakKey设置为空字符，如果提前退出会导致提前中止条件
        // To get this value, breakkey must be set to null string
        invaildWord: matchCount !== inputValue.length,
    };
};

export const isMatchForceResultConfig = (config: MatchResultConfig, matchNum: number, inputValueLen: number) => {
    return matchNum === config.matchNum && inputValueLen >= config.min && inputValueLen <= config.max && (config.need !== undefined ? config.need : true);
};

export type MatchResultConfig = {
    min: number;
    max: number;
    matchNum: number;
    value: IpasswordForce;
    need?: boolean;
    back?: IpasswordForce;
};
export type IpasswordForce = false | 'high' | 'middle' | 'low';
