<?php

declare(strict_types=1);

namespace ImovelZapAi\Documents\Application\Services;

use Illuminate\Support\Str;
use ImovelZapAi\Documents\Domain\Exceptions\DocumentException;
use ImovelZapAi\Documents\Infrastructure\Persistence\Eloquent\BusinessDocumentModel;
use ImovelZapAi\Documents\Infrastructure\Persistence\Eloquent\DocumentShareModel;

final class DocumentShareTokenService
{
    /** @return array{plain: string, model: DocumentShareModel, url: string} */
    public function issue(BusinessDocumentModel $document, string $versionId, ?\DateTimeInterface $expiresAt = null): array
    {
        $plain = Str::lower(Str::random(40));
        $signature = hash_hmac('sha256', $document->id.'|'.$plain, (string) config('app.key'));
        $expires = $expiresAt ?? now()->addDays((int) config('documents.token_ttl_days', 14));

        $model = DocumentShareModel::query()->create([
            'tenant_id' => $document->tenant_id,
            'business_document_id' => $document->id,
            'document_version_id' => $versionId,
            'token_prefix' => substr($plain, 0, 8),
            'token_hash' => hash('sha256', $plain),
            'signature' => $signature,
            'expires_at' => $expires,
        ]);

        return ['plain' => $plain, 'model' => $model, 'url' => $this->publicUrl($plain)];
    }

    public function publicUrl(string $plain): string
    {
        $base = rtrim((string) (config('documents.public_base_url') ?: config('app.url')), '/');

        return $base.'/d/'.$plain;
    }

    /** @return array{share: DocumentShareModel, document: BusinessDocumentModel} */
    public function resolve(string $plain): array
    {
        $share = DocumentShareModel::query()->where('token_hash', hash('sha256', $plain))->first();
        if ($share === null) {
            throw DocumentException::tokenInvalid();
        }

        $expected = hash_hmac('sha256', $share->business_document_id.'|'.$plain, (string) config('app.key'));
        if (! hash_equals($share->signature, $expected)) {
            throw DocumentException::tokenInvalid();
        }
        if ($share->revoked_at !== null || ($share->expires_at !== null && $share->expires_at->isPast())) {
            throw DocumentException::tokenInvalid();
        }

        $document = BusinessDocumentModel::query()->whereKey($share->business_document_id)->first();
        if ($document === null || ! $document->isPubliclyAccessible()) {
            throw DocumentException::tokenInvalid();
        }

        $share->forceFill(['last_used_at' => now()])->save();

        return ['share' => $share, 'document' => $document];
    }
}
