<?php

declare(strict_types=1);

namespace App\Modules\Administration\Services;

use App\Modules\Administration\Repositories\EmployeeDocumentRepository;
use RuntimeException;

final class EmployeeDocumentService
{
    public function __construct(
        private EmployeeDocumentRepository $documents,
        private EmployeeService $employees
    ) {
    }

    public function byEmployee(
        int $employeeId
    ): array {
        return $this->documents
            ->byEmployee($employeeId);
    }

    public function find(
        int $id
    ): ?array {
        return $this->documents
            ->find($id);
    }

    public function create(
        int $employeeId,
        array $data,
        array $file
    ): void {
        $employee =
            $this->employees->find(
                $employeeId
            );

        if ($employee === null) {
            throw new RuntimeException(
                'Employee not found.'
            );
        }

        $documentType = trim(
            (string) (
                $data['document_type']
                ?? ''
            )
        );

        $documentTitle = trim(
            (string) (
                $data['document_title']
                ?? ''
            )
        );

        if ($documentType === '') {
            throw new RuntimeException(
                'Document type is required.'
            );
        }

        if ($documentTitle === '') {
            throw new RuntimeException(
                'Document title is required.'
            );
        }

        $employeeCode = (string) (
            $employee['employee_id']
            ?? ''
        );

        if ($employeeCode === '') {
            throw new RuntimeException(
                'Employee ID code is missing.'
            );
        }

        $uploadedFile =
            $this->uploadDocument(
                $file,
                $employeeCode,
                $documentType
            );

        $this->documents->create([
            'employee_id' =>
                $employeeId,

            'document_type' =>
                $documentType,

            'document_title' =>
                $documentTitle,

            'document_number' =>
                $this->nullableString(
                    $data['document_number']
                    ?? null
                ),

            'issue_date' =>
                $this->nullableString(
                    $data['issue_date']
                    ?? null
                ),

            'expiry_date' =>
                $this->nullableString(
                    $data['expiry_date']
                    ?? null
                ),

            'issuing_authority' =>
                $this->nullableString(
                    $data['issuing_authority']
                    ?? null
                ),

            'file_path' =>
                $uploadedFile['file_path'],

            'original_file_name' =>
                $uploadedFile[
                    'original_file_name'
                ],

            'mime_type' =>
                $uploadedFile['mime_type'],

            'file_size' =>
                $uploadedFile['file_size'],

            'notes' =>
                $this->nullableString(
                    $data['notes']
                    ?? null
                ),

            'status' => 1,

            'uploaded_by' =>
                isset($_SESSION['user_id'])
                    ? (int) $_SESSION['user_id']
                    : null,

            'created_at' =>
                date('Y-m-d H:i:s'),

            'updated_at' =>
                date('Y-m-d H:i:s')
        ]);
    }

    public function deactivate(
        int $id
    ): void {
        $document =
            $this->documents->find($id);

        if ($document === null) {
            throw new RuntimeException(
                'Document not found.'
            );
        }

        $this->documents
            ->deactivate($id);
    }
    public function toggleStatus(
        int $id
    ): void {
        $document =
            $this->documents->find($id);

        if ($document === null) {
            throw new RuntimeException(
                'Document not found.'
            );
        }

        $currentStatus =
            (int) (
                $document['status']
                ?? 0
            );

        $newStatus =
            $currentStatus === 1
                ? 0
                : 1;

        $this->documents->updateStatus(
            $id,
            $newStatus
        );
    }
    private function uploadDocument(
        array $file,
        string $employeeCode,
        string $documentType
    ): array {
$uploadError = (int) (
    $file['error']
    ?? UPLOAD_ERR_NO_FILE
);

if ($uploadError !== UPLOAD_ERR_OK) {

    $uploadErrors = [
        UPLOAD_ERR_INI_SIZE =>
            'The uploaded file exceeds the server upload_max_filesize limit.',

        UPLOAD_ERR_FORM_SIZE =>
            'The uploaded file exceeds the form size limit.',

        UPLOAD_ERR_PARTIAL =>
            'The file was only partially uploaded.',

        UPLOAD_ERR_NO_FILE =>
            'No document file was received by the server.',

        UPLOAD_ERR_NO_TMP_DIR =>
            'The server temporary upload directory is missing.',

        UPLOAD_ERR_CANT_WRITE =>
            'The server failed to write the uploaded file to disk.',

        UPLOAD_ERR_EXTENSION =>
            'A PHP extension stopped the file upload.'
    ];

    throw new RuntimeException(
        $uploadErrors[$uploadError]
        ?? 'Unknown upload error code: '
            . $uploadError
    );
}

        $maximumSize =
            10 * 1024 * 1024;

        $fileSize =
            (int) ($file['size'] ?? 0);

        if ($fileSize < 1) {
            throw new RuntimeException(
                'Uploaded document is empty.'
            );
        }

        if ($fileSize > $maximumSize) {
            throw new RuntimeException(
                'Document must not exceed 10 MB.'
            );
        }

        $temporaryFile =
            (string) (
                $file['tmp_name']
                ?? ''
            );

        if (
            $temporaryFile === ''
            || !is_uploaded_file(
                $temporaryFile
            )
        ) {
            throw new RuntimeException(
                'Invalid uploaded document.'
            );
        }

$mimeType = null;

/*
|--------------------------------------------------------------------------
| METHOD 1: finfo
|--------------------------------------------------------------------------
*/

if (class_exists('\finfo')) {

    $fileInfo = new \finfo(
        FILEINFO_MIME_TYPE
    );

    $detectedType = $fileInfo->file(
        $temporaryFile
    );

    if (
        is_string($detectedType)
        && $detectedType !== ''
    ) {
        $mimeType = $detectedType;
    }
}


/*
|--------------------------------------------------------------------------
| METHOD 2: mime_content_type fallback
|--------------------------------------------------------------------------
*/

if (
    $mimeType === null
    && function_exists('mime_content_type')
) {

    $detectedType =
        \mime_content_type(
            $temporaryFile
        );

    if (
        is_string($detectedType)
        && $detectedType !== ''
    ) {
        $mimeType = $detectedType;
    }
}


/*
|--------------------------------------------------------------------------
| METHOD 3: Browser MIME fallback
|--------------------------------------------------------------------------
*/

if ($mimeType === null) {

    $browserMime = trim(
        (string) (
            $file['type']
            ?? ''
        )
    );

    if ($browserMime !== '') {
        $mimeType = $browserMime;
    }
}


/*
|--------------------------------------------------------------------------
| MIME TYPE MUST EXIST
|--------------------------------------------------------------------------
*/

if ($mimeType === null) {

    throw new RuntimeException(
        'Unable to determine document type.'
    );
}

        $allowedTypes = [
            'application/pdf' =>
                'pdf',

            'image/jpeg' =>
                'jpg',

            'image/png' =>
                'png',

            'application/msword' =>
                'doc',

            'application/vnd.openxmlformats-officedocument.wordprocessingml.document' =>
                'docx'
        ];

        if (
            !isset(
                $allowedTypes[$mimeType]
            )
        ) {
            throw new RuntimeException(
                'Only PDF, JPG, PNG, DOC and DOCX files are allowed.'
            );
        }

        $extension =
            $allowedTypes[$mimeType];

        $directory =
            PUBLIC_PATH
            . '/uploads/employees/'
            . $employeeCode
            . '/documents';

        if (
            !is_dir($directory)
            && !mkdir(
                $directory,
                0755,
                true
            )
            && !is_dir($directory)
        ) {
            throw new RuntimeException(
                'Unable to create employee document directory.'
            );
        }

        if (!is_writable($directory)) {
            throw new RuntimeException(
                'Employee document directory is not writable.'
            );
        }

        $safeType = strtoupper(
            preg_replace(
                '/[^A-Za-z0-9]+/',
                '_',
                $documentType
            )
        );

        $safeType = trim(
            $safeType,
            '_'
        );

        if ($safeType === '') {
            $safeType = 'DOCUMENT';
        }

        $fileName =
            $employeeCode
            . '_'
            . $safeType
            . '_'
            . date('Ymd_His')
            . '_'
            . bin2hex(
                random_bytes(4)
            )
            . '.'
            . $extension;

        $destination =
            $directory
            . '/'
            . $fileName;

        if (
            !move_uploaded_file(
                $temporaryFile,
                $destination
            )
        ) {
            throw new RuntimeException(
                'Unable to save employee document.'
            );
        }

        return [
            'file_path' =>
                'uploads/employees/'
                . $employeeCode
                . '/documents/'
                . $fileName,

            'original_file_name' =>
                (string) (
                    $file['name']
                    ?? $fileName
                ),

            'mime_type' =>
                $mimeType,

            'file_size' =>
                $fileSize
        ];
    }

    private function nullableString(
        mixed $value
    ): ?string {
        $value = trim(
            (string) $value
        );

        return $value !== ''
            ? $value
            : null;
    }
}