<?php

declare(strict_types=1);

namespace Core;

final class Validator
{
    private static ?array $appConfig = null;

    public static function admission(array $input, array $files, bool $isUpdate = false): array
    {
        $errors = [];
        $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.';
        }

        $config = self::appConfig();
        if (!empty($input['unit_name'] ?? '') && !in_array((string) $input['unit_name'], $config['units'] ?? [], true)) {
            $errors['unit_name'] = 'Selecione uma unidade válida.';
        }
        if (!empty($input['job_title'] ?? '') && !in_array((string) $input['job_title'], $config['job_titles'] ?? [], true)) {
            $errors['job_title'] = 'Selecione um cargo 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) {
            $academicRequired = [
                'internship_nature','academic_level','institution_name','institution_cnpj','institution_address','institution_neighborhood','institution_number','institution_zip','institution_city','institution_state','advisor_name','semester','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.';
        }

        return $errors;
    }

    private static function appConfig(): array
    {
        if (self::$appConfig === null) {
            self::$appConfig = require dirname(__DIR__) . '/config/app.php';
        }

        return self::$appConfig;
    }

    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 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;
    }
}
