/**
 * validators.spec.js — T#7 (cobertura dos helpers de sanitização do external intake)
 *
 * O módulo validators.js é a base de toda sanitização da página pública de
 * cadastro (CadastroPacienteExterno) e de várias outras páginas internas.
 * Esses testes garantem o contrato: o backend (RPC create_patient_intake_request_v2)
 * confia que os dígitos vêm normalizados, datas em ISO, emails sem espaço, etc.
 */
import { describe, it, expect } from 'vitest';
import {
    digitsOnly,
    isValidCPF,
    fmtCPF,
    generateCPF,
    isValidCNPJ,
    fmtCNPJ,
    fmtRG,
    isValidPhone,
    fmtPhone,
    isValidEmail,
    isValidCEP,
    fmtCEP,
    sanitizeDigits,
    toISODate
} from '../validators.js';

describe('digitsOnly — base de toda sanitização', () => {
    it('extrai só dígitos de strings com máscara', () => {
        expect(digitsOnly('(11) 98765-4321')).toBe('11987654321');
        expect(digitsOnly('123.456.789-01')).toBe('12345678901');
        expect(digitsOnly('00000-000')).toBe('00000000');
    });

    it('retorna string vazia para null/undefined/vazio', () => {
        expect(digitsOnly(null)).toBe('');
        expect(digitsOnly(undefined)).toBe('');
        expect(digitsOnly('')).toBe('');
        expect(digitsOnly('   ')).toBe('');
        expect(digitsOnly('abc')).toBe('');
    });

    it('preserva apenas os dígitos em qualquer string', () => {
        expect(digitsOnly('a1b2c3')).toBe('123');
        expect(digitsOnly('R$ 1.234,56')).toBe('123456');
    });
});

describe('isValidCPF — algoritmo completo', () => {
    it('aceita CPFs válidos com e sem máscara', () => {
        expect(isValidCPF('529.982.247-25')).toBe(true);
        expect(isValidCPF('52998224725')).toBe(true);
    });

    it('rejeita comprimento incorreto', () => {
        expect(isValidCPF('123')).toBe(false);
        expect(isValidCPF('12345678901234')).toBe(false);
        expect(isValidCPF('')).toBe(false);
    });

    it('rejeita sequências repetidas (regressão fraude clássica)', () => {
        expect(isValidCPF('11111111111')).toBe(false);
        expect(isValidCPF('00000000000')).toBe(false);
        expect(isValidCPF('99999999999')).toBe(false);
    });

    it('rejeita dígitos verificadores inválidos', () => {
        expect(isValidCPF('52998224726')).toBe(false); // último dígito errado
        expect(isValidCPF('52998224715')).toBe(false); // penúltimo dígito errado
    });

    it('aceita CPF gerado por generateCPF (round-trip)', () => {
        for (let i = 0; i < 20; i++) {
            const gen = generateCPF();
            expect(isValidCPF(gen)).toBe(true);
        }
    });
});

describe('fmtCPF — formatação visual', () => {
    it('formata 11 dígitos como 000.000.000-00', () => {
        expect(fmtCPF('52998224725')).toBe('529.982.247-25');
    });

    it('aceita parcial e formata até onde tem', () => {
        expect(fmtCPF('123')).toBe('123');
        expect(fmtCPF('1234')).toBe('123.4');
        expect(fmtCPF('123456')).toBe('123.456');
        expect(fmtCPF('1234567')).toBe('123.456.7');
    });

    it('trunca em 11 dígitos', () => {
        expect(fmtCPF('5299822472512345')).toBe('529.982.247-25');
    });

    it('vazio para null/empty', () => {
        expect(fmtCPF('')).toBe('');
        expect(fmtCPF(null)).toBe('');
    });
});

describe('isValidCNPJ — algoritmo completo', () => {
    it('aceita CNPJs válidos com e sem máscara', () => {
        expect(isValidCNPJ('11.222.333/0001-81')).toBe(true);
        expect(isValidCNPJ('11222333000181')).toBe(true);
    });

    it('rejeita sequências repetidas', () => {
        expect(isValidCNPJ('11111111111111')).toBe(false);
    });

    it('rejeita comprimento incorreto', () => {
        expect(isValidCNPJ('123')).toBe(false);
        expect(isValidCNPJ('11.222.333/0001-')).toBe(false);
    });
});

describe('fmtCNPJ — formatação visual', () => {
    it('formata 14 dígitos como 00.000.000/0000-00', () => {
        expect(fmtCNPJ('11222333000181')).toBe('11.222.333/0001-81');
    });

    it('vazio para input vazio', () => {
        expect(fmtCNPJ('')).toBe('');
    });
});

describe('fmtRG — formatação visual', () => {
    it('formata 9 dígitos', () => {
        expect(fmtRG('123456789')).toBe('12.345.678-9');
    });

    it('vazio para vazio/null', () => {
        expect(fmtRG('')).toBe('');
        expect(fmtRG(null)).toBe('');
    });
});

describe('isValidPhone — telefones BR', () => {
    it('aceita 10 dígitos (fixo) e 11 (celular)', () => {
        expect(isValidPhone('1133334444')).toBe(true);    // fixo
        expect(isValidPhone('11933334444')).toBe(true);   // celular
        expect(isValidPhone('(11) 9 3333-4444')).toBe(true); // com máscara
    });

    it('rejeita comprimentos errados', () => {
        expect(isValidPhone('123')).toBe(false);
        expect(isValidPhone('123456789012')).toBe(false);
        expect(isValidPhone('')).toBe(false);
    });
});

describe('fmtPhone — formatação visual', () => {
    it('formata celular (11 dígitos) como (XX) XXXXX-XXXX', () => {
        expect(fmtPhone('11987654321')).toBe('(11) 98765-4321');
    });

    it('formata fixo (10 dígitos) como (XX) XXXX-XXXX', () => {
        expect(fmtPhone('1133334444')).toBe('(11) 3333-4444');
    });

    it('retorna dígitos crus para tamanhos não suportados', () => {
        expect(fmtPhone('123')).toBe('123');
    });

    it('vazio para vazio', () => {
        expect(fmtPhone('')).toBe('');
        expect(fmtPhone(null)).toBe('');
    });
});

describe('isValidEmail — formato mínimo', () => {
    it('aceita emails com formato razoável', () => {
        expect(isValidEmail('user@test.com')).toBe(true);
        expect(isValidEmail('a@b.c')).toBe(true);
        expect(isValidEmail('  user@test.com  ')).toBe(true); // trim aplicado
    });

    it('rejeita vazios e null', () => {
        expect(isValidEmail('')).toBe(false);
        expect(isValidEmail(null)).toBe(false);
        expect(isValidEmail(undefined)).toBe(false);
        expect(isValidEmail('   ')).toBe(false);
    });

    it('rejeita formatos sem @ ou sem domínio', () => {
        expect(isValidEmail('user')).toBe(false);
        expect(isValidEmail('user@')).toBe(false);
        expect(isValidEmail('@host.com')).toBe(false);
        expect(isValidEmail('user@host')).toBe(false);
    });
});

describe('isValidCEP — 8 dígitos', () => {
    it('aceita CEP de 8 dígitos com ou sem máscara', () => {
        expect(isValidCEP('01310-100')).toBe(true);
        expect(isValidCEP('01310100')).toBe(true);
    });

    it('rejeita comprimentos errados', () => {
        expect(isValidCEP('123')).toBe(false);
        expect(isValidCEP('12345-67')).toBe(false);
    });
});

describe('fmtCEP — formatação visual', () => {
    it('formata 8 dígitos como 00000-000', () => {
        expect(fmtCEP('01310100')).toBe('01310-100');
    });

    it('parcial mantém prefixo formatado', () => {
        expect(fmtCEP('01310')).toBe('01310');
        expect(fmtCEP('013101')).toBe('01310-1');
    });

    it('trunca em 8', () => {
        expect(fmtCEP('0131010012345')).toBe('01310-100');
    });
});

describe('sanitizeDigits — preparação para o banco', () => {
    it('extrai dígitos ou retorna null para vazio', () => {
        expect(sanitizeDigits('(11) 98765-4321')).toBe('11987654321');
        expect(sanitizeDigits('')).toBe(null);
        expect(sanitizeDigits(null)).toBe(null);
        expect(sanitizeDigits('abc')).toBe(null);
    });
});

describe('toISODate — DD/MM/YYYY ou DD-MM-YYYY → YYYY-MM-DD', () => {
    it('converte DD/MM/YYYY → ISO', () => {
        expect(toISODate('15/03/1990')).toBe('1990-03-15');
    });

    it('converte DD-MM-YYYY → ISO', () => {
        expect(toISODate('15-03-1990')).toBe('1990-03-15');
    });

    it('retorna null para formato inválido', () => {
        expect(toISODate('1990-03-15')).toBe(null); // já em ISO, mas formato esperado é DD/MM/YYYY
        expect(toISODate('15/3/1990')).toBe(null);  // mês com 1 dígito
        expect(toISODate('abc')).toBe(null);
        expect(toISODate('')).toBe(null);
        expect(toISODate(null)).toBe(null);
    });

    it('rejeita data inexistente (mes 13)', () => {
        // Date constructor faz wrap silencioso; testamos comportamento atual.
        // 13/13/2020 → Date('2020-13-13') é Invalid Date → null
        expect(toISODate('13/13/2020')).toBe(null);
    });
});
