<?php

namespace App\Services\Candidate;

use CodeIgniter\Files\File;
use CodeIgniter\HTTP\Files\UploadedFile;

/**
 * Enforces the file-upload rules from 09-security-plan.md §3: MIME/extension
 * allow-list, size cap, random filename (never trust the original name for
 * the stored path), stored outside the public web root.
 */
class ResumeUploadService
{
    private const ALLOWED_EXTENSIONS = ['pdf', 'doc', 'docx'];
    private const ALLOWED_MIME_TYPES = [
        'application/pdf',
        'application/msword',
        'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
    ];
    private const MAX_SIZE_KB = 5120; // 5MB

    public function validateAndStore(UploadedFile $file): array
    {
        if (! $file->isValid()) {
            throw new \RuntimeException('Upload failed: ' . $file->getErrorString());
        }

        $extension = strtolower($file->getClientExtension());
        if (! in_array($extension, self::ALLOWED_EXTENSIONS, true)) {
            throw new \InvalidArgumentException('Only PDF, DOC, and DOCX files are accepted.');
        }

        $mime = $file->getMimeType();
        if (! in_array($mime, self::ALLOWED_MIME_TYPES, true)) {
            throw new \InvalidArgumentException('File content does not match an accepted resume format.');
        }

        if ($file->getSizeByUnit('kb') > self::MAX_SIZE_KB) {
            throw new \InvalidArgumentException('Resume file exceeds the 5MB size limit.');
        }

        $storageDir = WRITEPATH . 'uploads/resumes';
        if (! is_dir($storageDir)) {
            mkdir($storageDir, 0755, true);
        }

        // Random UUID-style filename — never trust/reuse the original name
        // for the stored path (see security plan §3).
        $randomName = bin2hex(random_bytes(16)) . '.' . $extension;
        $file->move($storageDir, $randomName);

        return [
            'file_path'         => 'uploads/resumes/' . $randomName,
            'original_filename' => $file->getClientName(),
        ];
    }

    /**
     * Resumes are served through a controller action that re-checks
     * ownership/unlock permission on every request — never a directly
     * browsable path. This returns the absolute disk path for that
     * controller to stream, only after the caller has already authorized
     * the request.
     */
    public function absolutePath(string $relativePath): string
    {
        return WRITEPATH . 'uploads/' . ltrim(str_replace('uploads/', '', $relativePath), '/');
    }
}
