export class Validate {
    /**
     * Valida o tipo de chave
     * @param key Chave Pix que deseja validar o tipo
     * @returns Objeto com a validação do tipo de chave
     */
    public validate(key: string): {cpf: boolean, cnpj: boolean, evp: boolean, email: boolean, phone: boolean} {
        return {
            cpf: this.cpf(key),
            cnpj: this.cnpj(key),
            evp: this.evp(key),
            email: this.email(key),
            phone: this.phone(key),
        }
    }

    /**
     * Retorna um array de KeyType com o tipo que a chave pode ser.
     * Util para validar quando a chave pode ser um CPF ou um CELULAR
     * Ex: 12945678969 Pode ser tanto um CPF quanto um celular
     * @param key Chave Pix que deseja validar
     * @returns Array com os tipos de chave que ela pode ser
     */
    public keyType(key: string): Array<KeyType> {
        const value = this.validate(key)
        // @ts-ignore
        return Object.keys(value).filter((type) => value[type]).map((v) => v as KeyType);
    }

    private cpf(cpf: string): boolean {
        cpf = cpf.replace(/[^\d]+/g, '');
        if (cpf.length !== 11 || !!cpf.match(/(\d)\1{10}/)) return false;
        const values = cpf.split('').map(el => +el);
        const rest = (count: number) => (values.slice(0, count-12).reduce( (soma, el, index) => (soma + el * (count-index)), 0 )*10) % 11 % 10;
        return rest(10) === values[9] && rest(11) === values[10];
    }

    private cnpj(cnpj: string): boolean {
        cnpj = cnpj.replace(/[^\d]+/g, '');
        if (cnpj.length !== 14 || !!cnpj.match(/(\d)\1{13}/)) return false;
        const values = cnpj.split('').map(el => +el);
        const rest = (count: number) => (values.slice(0, count-14).reverse().reduce( (soma, el, index) => (soma + el * (index+2<=9?index+2:index-6)), 0 )*10) % 11 % 10;
        return rest(12) === values[12] && rest(13) === values[13];
    }

    private evp(evp: string): boolean {
        return /[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/.test(evp);
    }

    private email(email: string): boolean {
        return /^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/.test(email);
    }

    private phone(phone: string): boolean {
        if (!phone.trim().startsWith('+')) phone = '+55'.concat(phone);
        phone = '+'.concat(phone.replace(/[^\d]+/g, ''));
        if (!/^\+[1-9][0-9]\d{1,14}$/.test(phone)) return false;
        if (!phone.startsWith('+55')) return true;

        // fonte: https://dados.gov.br/dataset/codigos-nacionais-cn
        const dddList = [
            '11','12','13','14','15','16','17','18','19','21','22','24','27','28','31','32','33','34','35','37','38','41',
            '42','43','44','45','46','47','48','49','51','53','54','55','61','62','63','64','65','66','67','68','69','71',
            '73','74','75','77','79','81','82','83','84','85','86','87','88','89','91','92','93','94','95','96','97','98','99'
        ];

        const ddd = phone.substring(3, 5);        
        return phone.length > 13 && phone[5] === '9' && !!dddList.find((item) => item === ddd);
    }
}