<?php

namespace Modules\Documents\Services;

use App\Models\ActivityLogModel;
use App\Models\DocumentTypeModel;
use App\Models\EmployeeDocumentModel;
use App\Models\EmployeeDocumentVersionModel;
use CodeIgniter\HTTP\Files\UploadedFile;
use Config\Uploads;

class EmployeeDocumentService
{
    protected EmployeeDocumentModel $documents;
    protected EmployeeDocumentVersionModel $versions;
    protected DocumentTypeModel $types;
    protected ActivityLogModel $activity;
    protected Uploads $uploadsConfig;

    public function __construct()
    {
        $this->documents     = new EmployeeDocumentModel();
        $this->versions      = new EmployeeDocumentVersionModel();
        $this->types         = new DocumentTypeModel();
        $this->activity      = new ActivityLogModel();
        $this->uploadsConfig = config('Uploads');
    }

    public function create(array $input, ?UploadedFile $file, ?int $actingUserId): array
    {
        if (! $file || ! $file->isValid() || $file->hasMoved()) {
            return ['success' => false, 'errors' => ['document_file' => 'A document file is required.']];
        }

        $employeeId     = (int) ($input['employee_id'] ?? 0);
        $documentTypeId = (int) ($input['document_type_id'] ?? 0);

        if ($employeeId <= 0 || $documentTypeId <= 0) {
            return ['success' => false, 'errors' => ['form' => 'Employee and document type are required.']];
        }

        $type = $this->types->find($documentTypeId);
        if (! $type || $type['status'] !== 'active') {
            return ['success' => false, 'errors' => ['document_type_id' => 'Document type not found.']];
        }

        $validation = $this->validateMetadata($input, $type);
        if (! $validation['success']) {
            return $validation;
        }

        $upload = $this->storeFile($file);
        if (! $upload['success']) {
            return ['success' => false, 'errors' => ['document_file' => $upload['error']]];
        }

        $data = [
            'employee_id'      => $employeeId,
            'document_type_id' => $documentTypeId,
            'document_number'  => trim($input['document_number'] ?? '') ?: null,
            'issue_date'       => trim($input['issue_date'] ?? '') ?: null,
            'expiry_date'      => trim($input['expiry_date'] ?? '') ?: null,
            'notes'            => $input['notes'] ?: null,
            'status'           => 'active',
            'version_count'    => 1,
            'created_by'       => $actingUserId,
            'updated_by'       => $actingUserId,
        ];

        if (! $this->documents->insert($data)) {
            $this->removeFile($upload['path']);

            return ['success' => false, 'errors' => $this->documents->errors()];
        }

        $id = $this->documents->getInsertID();

        $this->versions->insert([
            'employee_document_id' => $id,
            'version_number'       => 1,
            'file_path'            => $upload['path'],
            'original_filename'    => $upload['original'],
            'file_size'            => $upload['size'],
            'mime_type'            => $upload['mime'],
            'change_notes'         => trim($input['change_notes'] ?? '') ?: 'Initial upload',
            'uploaded_by'          => $actingUserId,
            'created_at'           => date('Y-m-d H:i:s'),
        ]);

        $this->activity->log(
            'employee_documents',
            'created',
            $actingUserId,
            "Uploaded {$type['name']} for employee #{$employeeId}",
            (string) $id
        );

        return ['success' => true, 'id' => $id];
    }

    public function update(int $id, array $input, ?UploadedFile $file, ?int $actingUserId): array
    {
        $document = $this->documents->getWithDetails($id);
        if (! $document) {
            return ['success' => false, 'errors' => ['id' => 'Document not found.']];
        }

        $type = $this->types->find($document['document_type_id']);
        $validation = $this->validateMetadata($input, $type, $document);
        if (! $validation['success']) {
            return $validation;
        }

        $data = [
            'document_number' => trim($input['document_number'] ?? '') ?: null,
            'issue_date'      => trim($input['issue_date'] ?? '') ?: null,
            'expiry_date'     => trim($input['expiry_date'] ?? '') ?: null,
            'notes'           => $input['notes'] ?: null,
            'updated_by'      => $actingUserId,
        ];

        if ($file && $file->isValid() && ! $file->hasMoved()) {
            $upload = $this->storeFile($file);
            if (! $upload['success']) {
                return ['success' => false, 'errors' => ['document_file' => $upload['error']]];
            }

            $versionNumber = $this->versions->nextVersionNumber($id);

            $this->versions->insert([
                'employee_document_id' => $id,
                'version_number'       => $versionNumber,
                'file_path'            => $upload['path'],
                'original_filename'    => $upload['original'],
                'file_size'            => $upload['size'],
                'mime_type'            => $upload['mime'],
                'change_notes'         => trim($input['change_notes'] ?? '') ?: "Version {$versionNumber}",
                'uploaded_by'          => $actingUserId,
                'created_at'           => date('Y-m-d H:i:s'),
            ]);

            $data['version_count'] = $versionNumber;

            $this->activity->log(
                'employee_documents',
                'version_uploaded',
                $actingUserId,
                "Uploaded version {$versionNumber} for document #{$id}",
                (string) $id
            );
        }

        if (! $this->documents->update($id, $data)) {
            return ['success' => false, 'errors' => $this->documents->errors()];
        }

        $this->activity->log('employee_documents', 'updated', $actingUserId, "Updated document #{$id}", (string) $id);

        return ['success' => true];
    }

    public function delete(int $id, ?int $actingUserId): array
    {
        $document = $this->documents->find($id);
        if (! $document) {
            return ['success' => false, 'error' => 'Document not found.'];
        }

        $this->documents->update($id, ['status' => 'archived', 'updated_by' => $actingUserId]);
        $this->activity->log('employee_documents', 'archived', $actingUserId, "Archived document #{$id}", (string) $id);

        return ['success' => true];
    }

    public function getDownloadPath(int $documentId, ?int $versionId = null): ?array
    {
        $document = $this->documents->find($documentId);
        if (! $document || $document['status'] !== 'active') {
            return null;
        }

        $version = $versionId
            ? $this->versions->getByVersion($documentId, $versionId)
            : $this->versions->getLatest($documentId);

        if (! $version) {
            return null;
        }

        $absolute = $this->resolveAbsolutePath($version['file_path']);
        if (! is_file($absolute)) {
            return null;
        }

        return [
            'absolute'  => $absolute,
            'filename'  => $version['original_filename'],
            'mime_type' => $version['mime_type'] ?: 'application/octet-stream',
        ];
    }

    protected function validateMetadata(array $input, array $type, ?array $existing = null): array
    {
        $documentNumber = trim($input['document_number'] ?? '') ?: null;
        $expiryDate     = trim($input['expiry_date'] ?? '') ?: null;

        if ($type['requires_number'] && $documentNumber === null) {
            return ['success' => false, 'errors' => ['document_number' => 'Document number is required for this type.']];
        }

        if ($type['requires_expiry'] && $expiryDate === null) {
            return ['success' => false, 'errors' => ['expiry_date' => 'Expiry date is required for this type.']];
        }

        return ['success' => true];
    }

    protected function storeFile(UploadedFile $file): array
    {
        if (! in_array($file->getMimeType(), $this->uploadsConfig->employeeDocumentAllowedMimes, true)) {
            return ['success' => false, 'error' => 'File type not allowed. Use PDF, JPEG, PNG, or WEBP.'];
        }

        if ($file->getSizeByUnit('kb') > $this->uploadsConfig->employeeDocumentMaxSizeKB) {
            return ['success' => false, 'error' => 'File must be smaller than ' . $this->uploadsConfig->employeeDocumentMaxSizeKB . 'KB.'];
        }

        $path = $this->uploadsConfig->employeeDocumentUploadPath;
        if (! is_dir($path)) {
            mkdir($path, 0755, true);
        }

        $newName = $file->getRandomName();
        $original = $file->getClientName();
        $size     = (int) $file->getSize();
        $mime     = $file->getMimeType();
        $file->move($path, $newName);

        return [
            'success'  => true,
            'path'     => rtrim($this->uploadsConfig->employeeDocumentPublicPath, '/') . '/' . $newName,
            'original' => $original,
            'size'     => $size,
            'mime'     => $mime,
        ];
    }

    protected function resolveAbsolutePath(string $relativePath): string
    {
        $expectedPrefix = rtrim(str_replace('\\', '/', $this->uploadsConfig->employeeDocumentPublicPath), '/');
        $normalized     = ltrim(str_replace('\\', '/', $relativePath), '/');

        if (! str_starts_with($normalized, $expectedPrefix)) {
            return '';
        }

        return FCPATH . $normalized;
    }

    protected function removeFile(string $relativePath): void
    {
        $absolute = $this->resolveAbsolutePath($relativePath);
        if ($absolute && is_file($absolute)) {
            @unlink($absolute);
        }
    }
}
