<?php

namespace App\Services\Legal;

use App\Models\Customer;
use App\Models\DpaAcceptance;
use App\Models\DpaVersion;
use Illuminate\Support\Facades\Auth;

/**
 * Which agreement is in force, and whether a customer has accepted it.
 *
 * One place, because the same questions are asked by the customer's own page, by
 * the console's register and by anything that later chases the ones outstanding
 * — and three implementations of "has this customer accepted?" is three chances
 * to answer differently a question that is evidence.
 *
 * ## Accepting an OLD version is not accepting
 *
 * A new version supersedes the last, so this asks about the version in force and
 * nothing else. The old acceptance is kept — it was true when it was made, and
 * the history is the point — but it stops satisfying the question.
 */
final class ProcessingAgreement
{
    public function current(): ?DpaVersion
    {
        return DpaVersion::current();
    }

    /** The acceptance of the version in force, if there is one. */
    public function acceptanceOf(?Customer $customer): ?DpaAcceptance
    {
        $current = $this->current();

        if ($customer === null || $current === null) {
            return null;
        }

        return DpaAcceptance::query()
            ->where('customer_id', $customer->id)
            ->where('dpa_version_id', $current->id)
            ->first();
    }

    public function accepted(?Customer $customer): bool
    {
        return $this->acceptanceOf($customer) !== null;
    }

    /**
     * Record an acceptance.
     *
     * firstOrCreate over the pair the unique index covers: pressing twice is not
     * two agreements, and a double click must not become a duplicate-key error
     * in front of somebody agreeing to a contract.
     *
     * The address is read here rather than passed in, so no caller can record an
     * acceptance as coming from somewhere it invented.
     */
    public function accept(Customer $customer, ?string $ip = null): ?DpaAcceptance
    {
        $current = $this->current();

        if ($current === null) {
            return null;
        }

        return DpaAcceptance::query()->firstOrCreate(
            ['customer_id' => $customer->id, 'dpa_version_id' => $current->id],
            [
                'user_id' => Auth::id(),
                'ip' => $ip ?? request()->ip(),
                'accepted_at' => now(),
            ],
        );
    }
}
