<?php

namespace App\Domain\Document\Services;

use App\Domain\Document\DocumentSettings;
use App\Domain\Document\Enums\DocumentStatus;
use App\Domain\Document\Enums\DocumentType;
use App\Domain\Document\Models\Document;
use App\Domain\Participant\Models\Participant;
use App\Domain\Shared\Models\SystemSetting;
use App\Models\User;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;

class DocumentService
{
    public function upload(
        Model $documentable,
        UploadedFile $file,
        DocumentType $type,
        ?string $notes,
        User $actor
    ): Document {
        return DB::transaction(function () use ($documentable, $file, $type, $notes, $actor) {
            $uuid = (string) Str::uuid();
            $extension = $file->getClientOriginalExtension();
            $path = $this->buildStoragePath($documentable, $uuid, $extension);

            Storage::disk('local')->put($path, file_get_contents($file->getRealPath()));

            $status = $type->requiresApproval()
                ? DocumentStatus::Pending
                : DocumentStatus::Approved;

            return Document::create([
                'academy_id' => app('current_academy')->id,
                'documentable_type' => get_class($documentable),
                'documentable_id' => $documentable->id,
                'document_type' => $type->value,
                'file_path' => $path,
                'original_filename' => $file->getClientOriginalName(),
                'mime_type' => $file->getMimeType(),
                'file_size' => $file->getSize(),
                'status' => $status->value,
                'notes' => $notes,
                'uploaded_by' => $actor->id,
                'created_by' => $actor->id,
            ]);
        });
    }

    public function approve(Document $document, ?string $expiresAt, User $approver): Document
    {
        return DB::transaction(function () use ($document, $expiresAt, $approver) {
            $document->update([
                'status' => DocumentStatus::Approved->value,
                'expires_at' => $expiresAt,
                'approved_by' => $approver->id,
                'approved_at' => now(),
                'rejection_reason' => null,
            ]);

            return $document->fresh();
        });
    }

    public function reject(Document $document, string $reason, User $actor): Document
    {
        return DB::transaction(function () use ($document, $reason, $actor) {
            $document->update([
                'status' => DocumentStatus::Rejected->value,
                'rejection_reason' => $reason,
                'approved_by' => $actor->id,
                'approved_at' => now(),
            ]);

            return $document->fresh();
        });
    }

    public function markExpired(Document $document): Document
    {
        $document->update(['status' => DocumentStatus::Expired->value]);
        return $document->fresh();
    }

    public function checkAndExpireDocuments(): int
    {
        return Document::where('status', DocumentStatus::Approved)
            ->whereNotNull('expires_at')
            ->where('expires_at', '<', now()->toDateString())
            ->update(['status' => DocumentStatus::Expired->value]);
    }

    public function participantHasValidMedical(Participant $participant): bool
    {
        return Document::where('documentable_type', Participant::class)
            ->where('documentable_id', $participant->id)
            ->where('document_type', DocumentType::MedicalCertificate)
            ->where('status', DocumentStatus::Approved)
            ->where(function ($q) {
                $q->whereNull('expires_at')
                    ->orWhere('expires_at', '>=', now()->toDateString());
            })
            ->exists();
    }

    public function getMissingRequiredDocuments(Participant $participant): array
    {
        $missing = [];

        if (SystemSetting::get(DocumentSettings::REQUIRE_MEDICAL_CERTIFICATE, false)) {
            if (!$this->participantHasValidMedical($participant)) {
                $missing[] = DocumentType::MedicalCertificate;
            }
        }

        return $missing;
    }

    public function getMaxFileSizeBytes(): int
    {
        $mb = (int) SystemSetting::get(DocumentSettings::MAX_DOCUMENT_SIZE_MB, 10);
        return $mb * 1024 * 1024;
    }

    public function getAllowedTypes(): array
    {
        $json = SystemSetting::get(DocumentSettings::ALLOWED_DOCUMENT_TYPES, DocumentSettings::DEFAULTS[DocumentSettings::ALLOWED_DOCUMENT_TYPES]);

        if (is_string($json)) {
            $json = json_decode($json, true) ?? [];
        }

        return array_filter(
            DocumentType::cases(),
            fn (DocumentType $type) => in_array($type->value, $json)
        );
    }

    private function buildStoragePath(Model $documentable, string $uuid, string $extension): string
    {
        $academyId = app('current_academy')->id;
        $type = class_basename($documentable);
        $entityId = $documentable->id;

        return "documents/{$academyId}/{$type}/{$entityId}/{$uuid}.{$extension}";
    }
}
