<?php

declare(strict_types=1);

namespace ImovelZapAi\Proposals\Application\Services;

use Illuminate\Support\Str;
use ImovelZapAi\Proposals\Domain\Exceptions\ProposalException;
use ImovelZapAi\Proposals\Infrastructure\Persistence\Eloquent\ProposalModel;
use ImovelZapAi\Proposals\Infrastructure\Persistence\Eloquent\ProposalShareModel;

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

        $model = ProposalShareModel::query()->create([
            'tenant_id' => $proposal->tenant_id,
            'proposal_id' => $proposal->id,
            'proposal_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('proposals.public_base_url') ?: config('app.url')), '/');

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

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

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

        $proposal = ProposalModel::query()->whereKey($share->proposal_id)->first();
        if ($proposal === null || ! $proposal->isPubliclyAccessible()) {
            throw ProposalException::tokenInvalid();
        }

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

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