<?php

declare(strict_types=1);

namespace Core;

final class Validator
{
    public static function admission(array $input, array $files, bool $isUpdate = false): array
    {
        $errors = [];
        $config = require dirname(__DIR__) . '/config/app.php';
        $jobTitle = trim((string) ($input['job_title'] ?? ''));
        $isInternEdu = $jobTitle === 'Estagiário Ed. Física';
        $professorJobs = $config['professor_jobs'] ?? [];
        $isProfessorEdu = in_array($jobTitle, $professorJobs, true);
        $required = [
            'consent' => 'Você precisa aceitar o termo para prosseguir.',
            'race' => 'Informe a raça/cor.',
            'unit_name' => 'Informe a unidade.',
            'job_title' => 'Informe o cargo.',
            'full_name' => 'Informe o nome completo.',
            'mother_name' => 'Informe o nome da mãe.',
            'birth_date' => 'Informe a data de nascimento.',
            'marital_status' => 'Informe o estado civil.',
            'education_level' => 'Informe a escolaridade.',
            'nationality' => 'Informe a nacionalidade.',
            'birth_city' => 'Informe o município de nascimento.',
            'personal_email' => 'Informe o e-mail pessoal.',
            'phone' => 'Informe o celular.',
            'emergency_contact' => 'Informe o contato de emergência.',
            'address_line' => 'Informe o endereço.',
            'address_number' => 'Informe o número do endereço.',
            'neighborhood' => 'Informe o bairro.',
            'zip_code' => 'Informe o CEP.',
            'city' => 'Informe a cidade.',
            'state' => 'Informe a UF.',
            'rg_number' => 'Informe o RG.',
            'rg_issue_date' => 'Informe a data de expedição do RG.',
            'rg_issuer' => 'Informe o órgão expedidor.',
            'cpf' => 'Informe o CPF.',
            'pis_number' => 'Informe o PIS/PASEP.',
            'pis_issue_date' => 'Informe a data de emissão do PIS.',
        ];

        foreach ($required as $field => $message) {
            if (trim((string)($input[$field] ?? '')) === '') {
                $errors[$field] = $message;
            }
        }

        if (!filter_var($input['personal_email'] ?? '', FILTER_VALIDATE_EMAIL)) {
            $errors['personal_email'] = 'Informe um e-mail válido.';
        }

        if (!self::isValidCpf($input['cpf'] ?? '')) {
            $errors['cpf'] = 'Informe um CPF válido.';
        }

        foreach (['birth_date', 'rg_issue_date', 'pis_issue_date'] as $dateField) {
            if (!self::isValidDate($input[$dateField] ?? '')) {
                $errors[$dateField] = 'Informe uma data válida.';
            }
        }

        if ((int)($input['has_dependents'] ?? 0) === 1) {
            $dependents = $input['dependents'] ?? [];
            if (empty($dependents)) {
                $errors['dependents'] = 'Informe ao menos um dependente.';
            }
            foreach ($dependents as $index => $dependent) {
                $n = $index + 1;
                if (trim((string)($dependent['full_name'] ?? '')) === '') $errors["dependents.$index.full_name"] = "Informe o nome do dependente $n.";
                if (!self::isValidDate($dependent['birth_date'] ?? '')) $errors["dependents.$index.birth_date"] = "Informe a data de nascimento do dependente $n.";
                if (!self::isValidCpf($dependent['cpf'] ?? '')) $errors["dependents.$index.cpf"] = "Informe o CPF válido do dependente $n.";
                if (trim((string)($dependent['birth_city'] ?? '')) === '') $errors["dependents.$index.birth_city"] = "Informe a cidade do dependente $n.";
            }
        }

        if ((int)($input['uses_transport_voucher'] ?? 0) === 1 && trim((string)($input['transport_voucher_daily_value'] ?? '')) === '') {
            $errors['transport_voucher_daily_value'] = 'Informe o valor diário do vale-transporte.';
        }

        if ((int)($input['is_student'] ?? 0) === 1 || $isInternEdu) {
            $academicRequired = [
                'internship_nature','academic_level','institution_name','institution_cnpj','institution_address','institution_neighborhood','institution_number','institution_zip','institution_city','institution_state','advisor_name','semester','study_shift','course_name','registration_number'
            ];
            foreach ($academicRequired as $field) {
                if (trim((string)($input['academic'][$field] ?? '')) === '') {
                    $errors["academic.$field"] = 'Campo acadêmico obrigatório.';
                }
            }
        }

        if ($isUpdate) {
            foreach (['admission_date', 'dismissal_date'] as $dateField) {
                $value = trim((string)($input[$dateField] ?? ''));
                if ($value !== '' && !self::isValidDate($value)) {
                    $errors[$dateField] = 'Informe uma data válida.';
                }
            }

            $salaryAmount = trim((string)($input['salary_amount'] ?? ''));
            $salaryType = trim((string)($input['salary_type'] ?? ''));
            if ($salaryAmount !== '' && !preg_match('/^\d+(?:[\.,]\d{1,2})?$/', $salaryAmount)) {
                $errors['salary_amount'] = 'Informe um salário válido.';
            }
            if ($salaryAmount !== '' && $salaryType === '') {
                $errors['salary_type'] = 'Selecione o tipo de salário.';
            }
        }

        foreach (['profile_photo','rg_front','rg_back'] as $field) {
            $missing = !isset($files[$field]) || (int)($files[$field]['error'] ?? UPLOAD_ERR_NO_FILE) === UPLOAD_ERR_NO_FILE;
            if (!$isUpdate && $missing) $errors[$field] = 'Este anexo é obrigatório.';
        }

        if ($isProfessorEdu) {
            if (trim((string) ($input['cref_number'] ?? '')) === '') {
                $errors['cref_number'] = 'Informe o número do CREF.';
            }
            if (!self::isValidCnpj($input['professor_mei_cnpj'] ?? '')) {
                $errors['professor_mei_cnpj'] = 'Informe um CNPJ válido para o professor.';
            }
            $missingCrefDoc = !isset($files['cref_document']) || (int)($files['cref_document']['error'] ?? UPLOAD_ERR_NO_FILE) === UPLOAD_ERR_NO_FILE;
            if (!$isUpdate && $missingCrefDoc) {
                $errors['cref_document'] = 'Envie o documento do CREF.';
            }
            $missingMeiCertificate = !isset($files['mei_certificate']) || (int)($files['mei_certificate']['error'] ?? UPLOAD_ERR_NO_FILE) === UPLOAD_ERR_NO_FILE;
            if (!$isUpdate && $missingMeiCertificate) {
                $errors['mei_certificate'] = 'Envie o Certificado Microempreendedor Individual (MEI).';
            }
        }

        $maritalStatus = trim((string) ($input['marital_status'] ?? ''));
        if ($maritalStatus === 'casado') {
            $missingMarriageCertificate = !isset($files['marriage_certificate']) || (int)($files['marriage_certificate']['error'] ?? UPLOAD_ERR_NO_FILE) === UPLOAD_ERR_NO_FILE;
            if (!$isUpdate && $missingMarriageCertificate) {
                $errors['marriage_certificate'] = 'Envie a certidão de casamento.';
            }
        }

        if ($maritalStatus === 'divorciado') {
            $missingDivorceAnnotation = !isset($files['divorce_annotation']) || (int)($files['divorce_annotation']['error'] ?? UPLOAD_ERR_NO_FILE) === UPLOAD_ERR_NO_FILE;
            if (!$isUpdate && $missingDivorceAnnotation) {
                $errors['divorce_annotation'] = 'Envie a averbação do divórcio.';
            }
        }

        if ((int) ($input['has_dependents'] ?? 0) === 1) {
            $dependents = $input['dependents'] ?? [];
            $certificateFiles = $files['dependent_birth_certificates'] ?? null;
            foreach ($dependents as $index => $dependent) {
                $file = self::extractNestedFile($certificateFiles, $index);
                $missingDependentCertificate = !$file || (int) ($file['error'] ?? UPLOAD_ERR_NO_FILE) === UPLOAD_ERR_NO_FILE;
                if (!$isUpdate && $missingDependentCertificate) {
                    $errors["dependent_birth_certificates.$index"] = 'Envie a certidão de nascimento deste dependente.';
                    continue;
                }
                if ($file && (int) ($file['error'] ?? UPLOAD_ERR_NO_FILE) !== UPLOAD_ERR_NO_FILE) {
                    $fileError = self::file($file, ['image/jpeg', 'image/png', 'application/pdf'], 5);
                    if ($fileError) {
                        $errors["dependent_birth_certificates.$index"] = $fileError;
                    }
                }
            }
        }

        return $errors;
    }


    private static function extractNestedFile(mixed $filesField, int $index): ?array
    {
        if (!is_array($filesField) || !isset($filesField['name'][$index])) {
            return null;
        }

        return [
            'name' => $filesField['name'][$index] ?? '',
            'type' => $filesField['type'][$index] ?? '',
            'tmp_name' => $filesField['tmp_name'][$index] ?? '',
            'error' => $filesField['error'][$index] ?? UPLOAD_ERR_NO_FILE,
            'size' => $filesField['size'][$index] ?? 0,
        ];
    }

    public static function file(array $file, array $allowedMimeTypes, int $maxSizeMb = 5): ?string
    {
        if (($file['error'] ?? UPLOAD_ERR_NO_FILE) === UPLOAD_ERR_NO_FILE) return null;
        if (($file['error'] ?? UPLOAD_ERR_OK) !== UPLOAD_ERR_OK) return 'Falha ao enviar o arquivo.';
        if (($file['size'] ?? 0) > ($maxSizeMb * 1024 * 1024)) return "O arquivo excede {$maxSizeMb} MB.";
        $finfo = new \finfo(FILEINFO_MIME_TYPE);
        $mime = $finfo->file($file['tmp_name']);
        if (!in_array($mime, $allowedMimeTypes, true)) return 'Tipo de arquivo inválido.';
        return null;
    }

    public static function isValidDate(?string $date): bool
    {
        if (!$date) return false;
        $parsed = \DateTimeImmutable::createFromFormat('Y-m-d', $date);
        return $parsed && $parsed->format('Y-m-d') === $date;
    }


    public static function isValidCnpj(?string $cnpj): bool
    {
        $cnpj = preg_replace('/\D/', '', (string)$cnpj);
        if (strlen($cnpj) !== 14 || preg_match('/(\d){13}/', $cnpj)) return false;
        $lengths = [12, 13];
        $weights = [
            [5,4,3,2,9,8,7,6,5,4,3,2],
            [6,5,4,3,2,9,8,7,6,5,4,3,2],
        ];
        foreach ($lengths as $idx => $length) {
            $sum = 0;
            for ($i = 0; $i < $length; $i++) $sum += (int)$cnpj[$i] * $weights[$idx][$i];
            $remainder = $sum % 11;
            $digit = $remainder < 2 ? 0 : 11 - $remainder;
            if ((int)$cnpj[$length] !== $digit) return false;
        }
        return true;
    }

    public static function isValidCpf(?string $cpf): bool
    {
        $cpf = preg_replace('/\D/', '', (string)$cpf);
        if (strlen($cpf) !== 11 || preg_match('/(\d)\1{10}/', $cpf)) return false;
        for ($t = 9; $t < 11; $t++) {
            $sum = 0;
            for ($i = 0; $i < $t; $i++) $sum += (int)$cpf[$i] * (($t + 1) - $i);
            $digit = ((10 * $sum) % 11) % 10;
            if ((int)$cpf[$t] !== $digit) return false;
        }
        return true;
    }
}
