<?php

declare(strict_types=1);

namespace App\Support;

use App\Models\Attachment;
use Core\Upload;
use Core\Validator;

final class AdmissionAttachments
{
    public static function validateSingle(?array $file, string $labelKey = 'profile_photo'): ?string
    {
        self::assertKnownAttachmentLabel($labelKey);

        if (!is_array($file) || (int) ($file['error'] ?? UPLOAD_ERR_NO_FILE) === UPLOAD_ERR_NO_FILE) {
            return null;
        }

        return Validator::file($file, AdmissionForm::mimeTypesForLabel($labelKey), AdmissionForm::maxUploadBytes());
    }

    /**
     * @return array{stored:?string,replaced:?string}
     */
    public static function syncSingle(int $admissionId, string $labelKey, array $file): array
    {
        self::assertKnownAttachmentLabel($labelKey);

        $payload = Upload::store($file, 'uploads/admissions', AdmissionForm::mimeTypesForLabel($labelKey), AdmissionForm::maxUploadBytes());
        $existing = Attachment::upsert($admissionId, $labelKey, $payload);

        return [
            'stored' => (string) $payload['relative_path'],
            'replaced' => !empty($existing['relative_path']) && $existing['relative_path'] !== $payload['relative_path']
                ? (string) $existing['relative_path']
                : null,
        ];
    }
    /**
     * @return array<string, string>
     */
    public static function validate(array $input, array $files, ?array $attachmentFields = null): array
    {
        $errors = [];
        $attachmentFields ??= AdmissionForm::attachmentFields();

        foreach ($attachmentFields as $field) {
            $file = $files[$field] ?? null;
            if (!is_array($file) || (int) ($file['error'] ?? UPLOAD_ERR_NO_FILE) === UPLOAD_ERR_NO_FILE) {
                continue;
            }

            $fileError = Validator::file($file, AdmissionForm::mimeTypesForLabel($field), AdmissionForm::maxUploadBytes());
            if ($fileError !== null) {
                $errors[$field] = $fileError;
            }
        }

        foreach (self::dependentUploadMap($input, $files) as $fieldKey => $file) {
            $fileError = Validator::file($file, AdmissionForm::documentMimeTypes(), AdmissionForm::maxUploadBytes());
            if ($fileError !== null) {
                $errors[$fieldKey] = $fileError;
            }
        }

        return $errors;
    }

    /**
     * @return array{stored:list<string>, replaced:list<string>, deleted:list<string>}
     */
    public static function sync(int $admissionId, array $input, array $files, ?array $attachmentFields = null): array
    {
        $storedPaths = [];
        $replacedPaths = [];
        $deletedPaths = [];
        $attachmentFields ??= AdmissionForm::attachmentFields();

        foreach ($attachmentFields as $field) {
            $file = $files[$field] ?? null;
            if (!is_array($file) || (int) ($file['error'] ?? UPLOAD_ERR_NO_FILE) === UPLOAD_ERR_NO_FILE) {
                continue;
            }

            $payload = Upload::store($file, 'uploads/admissions', AdmissionForm::mimeTypesForLabel($field), AdmissionForm::maxUploadBytes());
            $storedPaths[] = $payload['relative_path'];
            $existing = Attachment::upsert($admissionId, $field, $payload);
            if (!empty($existing['relative_path']) && $existing['relative_path'] !== $payload['relative_path']) {
                $replacedPaths[] = (string) $existing['relative_path'];
            }
        }

        $activeDependentLabels = array_map(
            static fn (int $index): string => AdmissionForm::dependentCertificateLabel($index),
            array_keys(AdmissionForm::dependentEntries($input))
        );

        foreach (self::dependentUploadMap($input, $files) as $fieldKey => $file) {
            $index = (int) substr($fieldKey, strrpos($fieldKey, '.') + 1);
            $labelKey = AdmissionForm::dependentCertificateLabel($index);

            $payload = Upload::store($file, 'uploads/admissions', AdmissionForm::documentMimeTypes(), AdmissionForm::maxUploadBytes());
            $storedPaths[] = $payload['relative_path'];
            $existing = Attachment::upsert($admissionId, $labelKey, $payload);
            if (!empty($existing['relative_path']) && $existing['relative_path'] !== $payload['relative_path']) {
                $replacedPaths[] = (string) $existing['relative_path'];
            }
        }

        foreach (Attachment::deleteDependentCertificatesNotIn($admissionId, $activeDependentLabels) as $deletedAttachment) {
            if (!empty($deletedAttachment['relative_path'])) {
                $deletedPaths[] = (string) $deletedAttachment['relative_path'];
            }
        }

        return [
            'stored' => array_values(array_unique(array_filter($storedPaths))),
            'replaced' => array_values(array_unique(array_filter($replacedPaths))),
            'deleted' => array_values(array_unique(array_filter($deletedPaths))),
        ];
    }

    /**
     * @return array<string, array{name:string,type:string,tmp_name:string,error:int,size:int}>
     */
    private static function dependentUploadMap(array $input, array $files): array
    {
        $uploads = [];
        $nestedFiles = $files['dependent_birth_certificates'] ?? null;

        foreach (array_keys(AdmissionForm::dependentEntries($input)) as $index) {
            $file = AdmissionForm::extractNestedUpload($nestedFiles, (int) $index);
            if (!$file || (int) ($file['error'] ?? UPLOAD_ERR_NO_FILE) === UPLOAD_ERR_NO_FILE) {
                continue;
            }

            $uploads['dependent_birth_certificates.' . (int) $index] = $file;
        }

        return $uploads;
    }


    private static function assertKnownAttachmentLabel(string $labelKey): void
    {
        $labelKey = trim($labelKey);

        if ($labelKey === '') {
            throw new \InvalidArgumentException('Rótulo de anexo inválido.');
        }

        if (in_array($labelKey, AdmissionForm::attachmentFields(), true)) {
            return;
        }

        if (preg_match('/^dependent_birth_certificate_\d+$/', $labelKey) === 1) {
            return;
        }

        throw new \InvalidArgumentException('Rótulo de anexo desconhecido: ' . $labelKey);
    }
}
