<?php

declare(strict_types=1);

namespace App\Support;

use Core\RequestLimits;

final class AdmissionForm
{

    public static function maxUploadBytes(): int
    {
        return RequestLimits::uploadMaxFileBytes();
    }

    public static function maxUploadLabel(): string
    {
        return RequestLimits::uploadMaxFileLabel();
    }

    public static function postMaxBytes(): int
    {
        return RequestLimits::postMaxBytes();
    }

    public static function postMaxLabel(): string
    {
        return RequestLimits::postMaxLabel();
    }

    /**
     * @return list<string>
     */
    public static function attachmentFields(): array
    {
        return [
            'profile_photo',
            ...self::documentAttachmentFields(),
        ];
    }

    /**
     * @return list<string>
     */
    public static function documentAttachmentFields(): array
    {
        return [
            'rg_front',
            'rg_back',
            'cref_document',
            'mei_certificate',
            'marriage_certificate',
            'divorce_annotation',
            'work_card',
            'residence_proof',
            'enrollment_declaration',
        ];
    }

    /**
     * @return list<string>
     */
    public static function profilePhotoMimeTypes(): array
    {
        return ['image/jpeg', 'image/png'];
    }

    /**
     * @return list<string>
     */
    public static function documentMimeTypes(): array
    {
        return ['application/pdf'];
    }

    /**
     * @return list<string>
     */
    public static function mimeTypesForLabel(string $labelKey): array
    {
        return $labelKey === 'profile_photo'
            ? self::profilePhotoMimeTypes()
            : self::documentMimeTypes();
    }

    public static function acceptAttributeForLabel(string $labelKey): string
    {
        return $labelKey === 'profile_photo' ? '.jpg,.jpeg,.png' : '.pdf';
    }

    public static function inputAcceptAttributeForLabel(string $labelKey): string
    {
        return $labelKey === 'profile_photo' ? '.jpg,.jpeg,.png' : '.pdf,.jpg,.jpeg,.png';
    }


    public static function normalizeComparableText(mixed $value): string
    {
        $value = trim((string) $value);
        if ($value === '') {
            return '';
        }

        $normalized = iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $value);
        if ($normalized === false) {
            $normalized = $value;
        }

        $normalized = strtolower($normalized);
        $normalized = preg_replace('/\s+/', ' ', $normalized) ?? $normalized;

        return trim($normalized);
    }

    public static function isInternEducationJob(mixed $jobTitle): bool
    {
        return self::normalizeComparableText($jobTitle) === self::normalizeComparableText('Estagiário Ed. Física');
    }

    public static function isProfessorJob(mixed $jobTitle, ?array $config = null): bool
    {
        $config ??= require dirname(__DIR__, 2) . '/config/app.php';
        $normalizedJobTitle = self::normalizeComparableText($jobTitle);
        if ($normalizedJobTitle === '') {
            return false;
        }

        foreach (($config['professor_jobs'] ?? []) as $configuredJobTitle) {
            if ($normalizedJobTitle === self::normalizeComparableText($configuredJobTitle)) {
                return true;
            }
        }

        return str_contains($normalizedJobTitle, 'professor');
    }

    public static function isStudentAdmission(array $admission): bool
    {
        return (int) ($admission['is_student'] ?? 0) === 1
            || self::isInternEducationJob($admission['job_title'] ?? '');
    }

    /**
     * @return list<string>
     */
    public static function requiredDocumentLabelsForAdmission(array $admission, array $dependents = []): array
    {
        $config = require dirname(__DIR__, 2) . '/config/app.php';
        $labels = [
            'rg_front',
            'rg_back',
            'work_card',
            'residence_proof',
        ];

        if (self::isStudentAdmission($admission)) {
            $labels[] = 'enrollment_declaration';
        }

        if (self::isProfessorJob($admission['job_title'] ?? '', $config)) {
            $labels[] = 'cref_document';
            $labels[] = 'mei_certificate';
        }

        $maritalStatus = (string) ($admission['marital_status'] ?? '');
        if ($maritalStatus === 'casado') {
            $labels[] = 'marriage_certificate';
        }

        if ($maritalStatus === 'divorciado') {
            $labels[] = 'divorce_annotation';
        }

        foreach (array_values(array_filter($dependents, static fn (mixed $dependent): bool => is_array($dependent))) as $index => $_dependent) {
            $labels[] = self::dependentCertificateLabel((int) $index);
        }

        return array_values(array_unique($labels));
    }

    public static function attachmentLabelText(string $labelKey): string
    {
        if (preg_match('/^dependent_birth_certificate_(\d+)$/', $labelKey, $matches) === 1) {
            return 'Certidão de nascimento do dependente ' . ((int) $matches[1] + 1);
        }

        $config = require dirname(__DIR__, 2) . '/config/app.php';
        return (string) (($config['attachment_labels'][$labelKey] ?? null) ?: $labelKey);
    }

    public static function documentHintText(string $labelKey): string
    {
        if (preg_match('/^dependent_birth_certificate_\d+$/', $labelKey) === 1) {
            return 'Envie PDF, JPG ou PNG. Se vier da câmera, a imagem será reduzida e convertida para PDF antes do envio.';
        }

        return match ($labelKey) {
            'rg_front', 'rg_back', 'work_card', 'residence_proof', 'enrollment_declaration', 'cref_document', 'mei_certificate', 'marriage_certificate', 'divorce_annotation'
                => 'Envie PDF, JPG ou PNG. Se vier da câmera, a imagem será reduzida e convertida para PDF antes do envio.',
            default => 'Envie o arquivo deste documento.',
        };
    }

    /**
     * @return list<array{label_key:string,label:string,hint:string,accept:string,attachment:?array}>
     */
    public static function documentUploadItemsForAdmission(array $admission, array $dependents = [], array $attachments = []): array
    {
        $attachmentMap = [];
        foreach ($attachments as $attachment) {
            if (!is_array($attachment) || empty($attachment['label_key'])) {
                continue;
            }

            $attachmentMap[(string) $attachment['label_key']] = $attachment;
        }

        $items = [];
        foreach (self::requiredDocumentLabelsForAdmission($admission, $dependents) as $labelKey) {
            $items[] = [
                'label_key' => $labelKey,
                'label' => self::attachmentLabelText($labelKey),
                'hint' => self::documentHintText($labelKey),
                'accept' => self::inputAcceptAttributeForLabel($labelKey),
                'attachment' => $attachmentMap[$labelKey] ?? null,
            ];
        }

        return $items;
    }


    public static function normalizeDigits(mixed $value): string
    {
        return preg_replace('/\D/', '', (string) $value) ?? '';
    }

    public static function normalizeNullableString(mixed $value): ?string
    {
        $value = trim((string) $value);
        return $value === '' ? null : $value;
    }

    public static function normalizeUpperState(mixed $value): ?string
    {
        $value = self::normalizeNullableString($value);
        return $value === null ? null : strtoupper($value);
    }

    public static function normalizeMoney(mixed $value): ?string
    {
        $value = preg_replace('/\s+/', '', (string) $value) ?? '';
        if ($value === '') {
            return null;
        }

        if (str_contains($value, ',') && str_contains($value, '.')) {
            $normalized = str_replace('.', '', $value);
            $normalized = str_replace(',', '.', $normalized);
        } else {
            $normalized = str_replace(',', '.', $value);
        }

        if (!is_numeric($normalized)) {
            return null;
        }

        return number_format((float) $normalized, 2, '.', '');
    }

    public static function resolveBankPaymentMethod(array $input): string
    {
        return in_array((string) ($input['bank_payment_method'] ?? 'bank'), ['bank', 'pix'], true)
            ? (string) ($input['bank_payment_method'] ?? 'bank')
            : 'bank';
    }

    public static function usesTransportVoucher(array $input): bool
    {
        return (int) ($input['uses_transport_voucher'] ?? 0) === 1;
    }

    public static function hasDependents(array $input): bool
    {
        return (int) ($input['has_dependents'] ?? 0) === 1;
    }

    /**
     * @return list<array>
     */
    public static function dependentEntries(array $input): array
    {
        if (!self::hasDependents($input) || !isset($input['dependents']) || !is_array($input['dependents'])) {
            return [];
        }

        return array_values(array_filter($input['dependents'], static fn (mixed $dependent): bool => is_array($dependent)));
    }

    /**
     * @return array<string, mixed>
     */
    public static function buildPublicPayload(array $input, array $server, string $cpf): array
    {
        $payload = self::buildBasePayload($input, $cpf);

        $bankPaymentMethod = self::resolveBankPaymentMethod($input);

        return array_merge($payload, [
            'consent' => 1,
            'employment_function' => null,
            'admission_date' => null,
            'dismissal_date' => null,
            'workload_hours' => null,
            'salary_amount' => null,
            'salary_type' => null,
            'bank_payment_method' => $bankPaymentMethod,
            'bank_agency' => $bankPaymentMethod === 'bank' ? self::normalizeNullableString($input['bank_agency'] ?? null) : null,
            'bank_account' => $bankPaymentMethod === 'bank' ? self::normalizeNullableString($input['bank_account'] ?? null) : null,
            'pix_key' => $bankPaymentMethod === 'pix' ? self::normalizeNullableString($input['pix_key'] ?? null) : null,
            'status' => 'recebido',
            'internal_notes' => null,
            'lgpd_consent_at' => date('Y-m-d H:i:s'),
            'submitted_ip' => $server['REMOTE_ADDR'] ?? null,
        ]);
    }

    /**
     * @return array<string, mixed>
     */
    public static function buildAdminPayload(array $input, string $cpf): array
    {
        $bankPaymentMethod = self::resolveBankPaymentMethod($input);
        $payload = self::buildBasePayload($input, $cpf);

        return array_merge($payload, [
            'employment_function' => self::normalizeNullableString($input['employment_function'] ?? null),
            'admission_date' => self::normalizeNullableString($input['admission_date'] ?? null),
            'dismissal_date' => self::normalizeNullableString($input['dismissal_date'] ?? null),
            'workload_hours' => self::normalizeNullableString($input['workload_hours'] ?? null),
            'salary_amount' => self::normalizeMoney($input['salary_amount'] ?? null),
            'salary_type' => self::normalizeNullableString($input['salary_type'] ?? null),
            'bank_payment_method' => $bankPaymentMethod,
            'bank_agency' => $bankPaymentMethod === 'bank' ? self::normalizeNullableString($input['bank_agency'] ?? null) : null,
            'bank_account' => $bankPaymentMethod === 'bank' ? self::normalizeNullableString($input['bank_account'] ?? null) : null,
            'pix_key' => $bankPaymentMethod === 'pix' ? self::normalizeNullableString($input['pix_key'] ?? null) : null,
            'status' => (string) ($input['status'] ?? 'recebido'),
            'internal_notes' => self::normalizeNullableString($input['internal_notes'] ?? null),
        ]);
    }

    /**
     * @return array<string, mixed>
     */
    private static function buildBasePayload(array $input, string $cpf): array
    {
        $usesTransportVoucher = self::usesTransportVoucher($input);
        $hasDependents = self::hasDependents($input);
        $isProfessorJob = self::isProfessorJob($input['job_title'] ?? '');

        return [
            'race' => (string) ($input['race'] ?? ''),
            'unit_name' => (string) ($input['unit_name'] ?? ''),
            'job_title' => (string) ($input['job_title'] ?? ''),
            'full_name' => (string) ($input['full_name'] ?? ''),
            'father_name' => self::normalizeNullableString($input['father_name'] ?? null),
            'mother_name' => (string) ($input['mother_name'] ?? ''),
            'birth_date' => (string) ($input['birth_date'] ?? ''),
            'marital_status' => (string) ($input['marital_status'] ?? ''),
            'is_student' => (int) ($input['is_student'] ?? 0),
            'education_level' => (string) ($input['education_level'] ?? ''),
            'nationality' => (string) ($input['nationality'] ?? ''),
            'place_of_birth' => self::normalizeNullableString($input['place_of_birth'] ?? null),
            'personal_email' => (string) ($input['personal_email'] ?? ''),
            'phone' => self::normalizeDigits($input['phone'] ?? ''),
            'emergency_contact' => self::normalizeDigits($input['emergency_contact'] ?? ''),
            'address_line' => (string) ($input['address_line'] ?? ''),
            'address_number' => (string) ($input['address_number'] ?? ''),
            'neighborhood' => (string) ($input['neighborhood'] ?? ''),
            'zip_code' => self::normalizeDigits($input['zip_code'] ?? ''),
            'city' => (string) ($input['city'] ?? ''),
            'address_extra' => self::normalizeNullableString($input['address_extra'] ?? null),
            'state' => self::normalizeUpperState($input['state'] ?? ''),
            'rg_number' => self::normalizeDigits($input['rg_number'] ?? ''),
            'rg_issue_date' => (string) ($input['rg_issue_date'] ?? ''),
            'rg_issuer' => (string) ($input['rg_issuer'] ?? ''),
            'birth_city' => (string) ($input['birth_city'] ?? ''),
            'birth_state' => self::normalizeUpperState($input['birth_state'] ?? ($input['state'] ?? '')),
            'cpf' => $cpf,
            'pis_number' => self::normalizeDigits($input['pis_number'] ?? ''),
            'pis_issue_date' => (string) ($input['pis_issue_date'] ?? ''),
            'has_dependents' => $hasDependents ? 1 : 0,
            'dependent_has_disability' => $hasDependents ? (int) ($input['dependent_has_disability'] ?? 0) : 0,
            'uses_transport_voucher' => $usesTransportVoucher ? 1 : 0,
            'transport_voucher_daily_value' => $usesTransportVoucher
                ? self::normalizeMoney($input['transport_voucher_daily_value'] ?? null)
                : null,
            'uniform_size' => self::normalizeNullableString($input['uniform_size'] ?? null),
            'cref_number' => $isProfessorJob
                ? self::normalizeNullableString($input['cref_number'] ?? null)
                : null,
            'professor_mei_cnpj' => $isProfessorJob && self::normalizeNullableString($input['professor_mei_cnpj'] ?? null) !== null
                ? self::normalizeDigits($input['professor_mei_cnpj'] ?? '')
                : null,
        ];
    }

    public static function normalizeAcademicPayload(?array $academic): ?array
    {
        if (!is_array($academic)) {
            return null;
        }

        return [
            'internship_nature' => trim((string) ($academic['internship_nature'] ?? '')),
            'academic_level' => trim((string) ($academic['academic_level'] ?? '')),
            'institution_name' => trim((string) ($academic['institution_name'] ?? '')),
            'institution_cnpj' => self::normalizeDigits($academic['institution_cnpj'] ?? ''),
            'institution_address' => trim((string) ($academic['institution_address'] ?? '')),
            'institution_neighborhood' => trim((string) ($academic['institution_neighborhood'] ?? '')),
            'institution_number' => trim((string) ($academic['institution_number'] ?? '')),
            'institution_zip' => self::normalizeDigits($academic['institution_zip'] ?? ''),
            'institution_city' => trim((string) ($academic['institution_city'] ?? '')),
            'institution_state' => self::normalizeUpperState($academic['institution_state'] ?? '') ?? '',
            'advisor_name' => trim((string) ($academic['advisor_name'] ?? '')),
            'semester' => trim((string) ($academic['semester'] ?? '')),
            'study_shift' => trim((string) ($academic['study_shift'] ?? '')),
            'course_name' => trim((string) ($academic['course_name'] ?? '')),
            'registration_number' => trim((string) ($academic['registration_number'] ?? '')),
        ];
    }

    public static function dependentCertificateLabel(int $index): string
    {
        return 'dependent_birth_certificate_' . $index;
    }

    /**
     * @return ?array{name:string,type:string,tmp_name:string,error:int,size:int}
     */
    public static function extractNestedUpload(?array $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,
        ];
    }
}
