<?php

namespace App\Models;

use App\Enum\Esign\EsignStatus;
use App\Trait\BelongsToSolicitor;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;

/**
 * One Fintrixor document — a single template instance for a single recipient.
 * A ContractRequest fans out into one per selected (mapped) contract.
 * `provider_document_id` (Fintrixor ULID) is the webhook correlation key.
 */
class EsignDocument extends Model
{
    use BelongsToSolicitor;

    protected $fillable = [
        'contract_request_id',
        'solicitor_id',
        'contract_id',
        'provider_document_id',
        'status',
        'recipient_name',
        'recipient_email',
        'signing_url',
        'draft_url',
        'document_hash',
        'certificate_id',
        'sent_at',
        'viewed_at',
        'signed_at',
        'completed_at',
        'expired_at',
        'failed_at',
        'meta',
    ];

    protected $casts = [
        'status' => EsignStatus::class,
        'meta' => 'array',
        'sent_at' => 'datetime',
        'viewed_at' => 'datetime',
        'signed_at' => 'datetime',
        'completed_at' => 'datetime',
        'expired_at' => 'datetime',
        'failed_at' => 'datetime',
    ];

    protected $attributes = [
        'status' => 'draft',
    ];

    public function contractRequest(): BelongsTo
    {
        return $this->belongsTo(ContractRequest::class);
    }

    public function contract(): BelongsTo
    {
        return $this->belongsTo(Contract::class);
    }

    public function esignEvents(): HasMany
    {
        return $this->hasMany(EsignEvent::class)->orderBy('occurred_at');
    }

    /**
     * Record the Fintrixor create-response: store its id + signing link and mark SENT.
     */
    public function markSent(array $providerDocument): void
    {
        $this->forceFill([
            'provider_document_id' => $providerDocument['id'] ?? $this->provider_document_id,
            'signing_url' => $providerDocument['signing_url'] ?? $this->signing_url,
            'status' => EsignStatus::SENT,
            'sent_at' => $this->sent_at ?? now(),
            'meta' => array_merge($this->meta ?? [], ['create_response' => $providerDocument]),
        ])->save();
    }

    public function markFailed(string $reason): void
    {
        $this->forceFill([
            'status' => EsignStatus::FAILED,
            'failed_at' => now(),
            'meta' => array_merge($this->meta ?? [], ['error' => $reason]),
        ])->save();
    }

    /**
     * Advance status from a webhook event without regressing a more-advanced
     * non-failure status (rank()). Copied from ContactMessage::transitionStatus().
     */
    public function transitionStatus(EsignStatus $next, array $timestamps = []): void
    {
        if ($this->status && $next->rank() < $this->status->rank() && $next->isTerminalFailure() === false) {
            if (!empty($timestamps)) {
                $this->forceFill($timestamps)->save();
            }
            return;
        }

        $this->forceFill(array_merge(['status' => $next], $timestamps))->save();
    }
}
