<?php

namespace App\Modules\Sales\Services;

use App\Modules\Sales\Exceptions\SalesReturnOperationalException;
use App\Modules\Sales\Models\SalesReturn;
use App\Modules\Sales\Repositories\SalesReturnRepository;
use App\Modules\Sales\Support\SalesReturnLifecycleRules;
use App\Modules\Sales\Support\SalesReturnStatus;
use Illuminate\Support\Facades\DB;

final class SalesReturnLifecycleService
{
    public function __construct(
        private readonly SalesReturnRepository $returns,
    ) {}

    public function approve(int $tenantId, int $returnId): void
    {
        DB::transaction(function () use ($tenantId, $returnId): void {
            $r = $this->lockedOrFail($tenantId, $returnId);
            $cur = SalesReturnStatus::tryFrom((string) $r->status)
                ?? throw new SalesReturnOperationalException('Unknown return status.', 'sales_return_bad_status');
            if (! SalesReturnLifecycleRules::allowsApprove($cur)) {
                throw new SalesReturnOperationalException('Approve not allowed.', 'sales_return_transition_invalid');
            }
            $r->forceFill(['status' => SalesReturnStatus::Approved->value]);
            $this->returns->save($r);
        });
    }

    public function markReceived(int $tenantId, int $returnId): void
    {
        DB::transaction(function () use ($tenantId, $returnId): void {
            $r = $this->lockedOrFail($tenantId, $returnId);
            $cur = SalesReturnStatus::tryFrom((string) $r->status)
                ?? throw new SalesReturnOperationalException('Unknown return status.', 'sales_return_bad_status');
            if (! SalesReturnLifecycleRules::allowsReceive($cur)) {
                throw new SalesReturnOperationalException('Receive not allowed.', 'sales_return_transition_invalid');
            }
            $r->forceFill(['status' => SalesReturnStatus::Received->value]);
            $this->returns->save($r);
        });
    }

    public function reject(int $tenantId, int $returnId): void
    {
        DB::transaction(function () use ($tenantId, $returnId): void {
            $r = $this->lockedOrFail($tenantId, $returnId);
            $cur = SalesReturnStatus::tryFrom((string) $r->status)
                ?? throw new SalesReturnOperationalException('Unknown return status.', 'sales_return_bad_status');
            if (! SalesReturnLifecycleRules::allowsReject($cur)) {
                throw new SalesReturnOperationalException('Reject not allowed.', 'sales_return_transition_invalid');
            }
            $r->forceFill(['status' => SalesReturnStatus::Rejected->value]);
            $this->returns->save($r);
        });
    }

    private function lockedOrFail(int $tenantId, int $returnId): SalesReturn
    {
        $r = $this->returns->lockByIdForTenant($tenantId, $returnId);
        if ($r === null) {
            throw new SalesReturnOperationalException('Return not found.', 'sales_return_not_found');
        }

        return $r;
    }
}
