<?php

namespace App\Services;

use App\Models\TransactionAttachment;
use App\Models\User;
use App\Models\Voucher;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
use Symfony\Component\HttpFoundation\StreamedResponse;

class VoucherAttachmentService
{
    public static function fileRules(bool $required = false): string
    {
        $extensions = implode(',', config('attachments.allowed_extensions', ['pdf', 'jpg', 'jpeg', 'png']));
        $maxKb = (int) config('attachments.max_size_kb', 10240);
        $prefix = $required ? 'required' : 'nullable';

        return "{$prefix}|file|mimes:{$extensions}|max:{$maxKb}";
    }

    public function disk(): string
    {
        return (string) config('attachments.disk', 'local');
    }

    public function canView(?User $user): bool
    {
        return $user && ($user->is_admin || $user->hasPermission('vouchers', 'view'));
    }

    public function canUpload(?User $user, Voucher $voucher): bool
    {
        if (!$user || $voucher->isCancelled()) {
            return false;
        }

        if ($user->is_admin) {
            return true;
        }

        return $user->hasPermission('vouchers', 'edit')
            || $user->hasPermission('vouchers', 'create');
    }

    public function canDelete(?User $user, TransactionAttachment $attachment): bool
    {
        if (!$user) {
            return false;
        }

        if ($user->is_admin) {
            return true;
        }

        if (!$user->hasPermission('vouchers', 'edit')) {
            return false;
        }

        return (int) $attachment->uploaded_by === (int) $user->id
            || $user->hasPermission('vouchers', 'delete');
    }

    public function store(Voucher $voucher, UploadedFile $file, ?string $description = null): TransactionAttachment
    {
        $companyId = (int) $voucher->company_id;
        $safeName = $this->sanitizeFileName($file->getClientOriginalName());
        $storedName = Str::uuid()->toString() . '_' . $safeName;
        $directory = "attachments/{$companyId}/vouchers/{$voucher->id}";
        $path = $file->storeAs($directory, $storedName, $this->disk());

        return TransactionAttachment::withoutGlobalScopes()->create([
            'company_id' => $companyId,
            'attachable_type' => $voucher->getMorphClass(),
            'attachable_id' => $voucher->id,
            'file_name' => $file->getClientOriginalName(),
            'file_path' => $path,
            'file_type' => $file->getClientMimeType() ?: $file->getMimeType(),
            'file_size' => (int) $file->getSize(),
            'description' => $description ? trim($description) : null,
            'uploaded_by' => auth()->id(),
        ]);
    }

    public function storeMany(Voucher $voucher, array $files, array $descriptions = []): array
    {
        $created = [];

        foreach ($files as $index => $file) {
            if (!$file instanceof UploadedFile || !$file->isValid()) {
                continue;
            }

            $created[] = $this->store(
                $voucher,
                $file,
                $descriptions[$index] ?? null,
            );
        }

        return $created;
    }

    public function delete(TransactionAttachment $attachment): void
    {
        $path = $attachment->file_path;
        $attachment->delete();

        if ($path && Storage::disk($this->disk())->exists($path)) {
            Storage::disk($this->disk())->delete($path);
        }
    }

    public function deleteAllForVoucher(Voucher $voucher): void
    {
        $attachments = TransactionAttachment::withoutGlobalScopes()
            ->where('company_id', $voucher->company_id)
            ->where('attachable_type', $voucher->getMorphClass())
            ->where('attachable_id', $voucher->id)
            ->get();

        foreach ($attachments as $attachment) {
            $this->delete($attachment);
        }
    }

    public function download(TransactionAttachment $attachment): StreamedResponse
    {
        if (!Storage::disk($this->disk())->exists($attachment->file_path)) {
            abort(404, 'File not found.');
        }

        return Storage::disk($this->disk())->download(
            $attachment->file_path,
            $attachment->file_name,
        );
    }

    private function sanitizeFileName(string $name): string
    {
        $name = basename(str_replace(['\\', '/'], DIRECTORY_SEPARATOR, $name));
        $name = preg_replace('/[^A-Za-z0-9._-]+/', '_', $name) ?: 'document';

        return Str::limit($name, 180, '');
    }
}
