<?php

declare(strict_types=1);

namespace App\Filament\Concerns;

use App\Exceptions\PlanLimitExceededException;
use App\Models\Company;
use App\Models\Subscription;
use Closure;
use Filament\Notifications\Notification;
use Illuminate\Support\Facades\Auth;

/**
 * Aplica los límites del plan en las páginas Create de Filament.
 *
 * Uso: en beforeCreate(), $this->guardPlanLimit(fn () => $service->assertCanAddX($company)).
 * Si se excede, muestra una notificación y aborta la creación (halt + rollback).
 */
trait EnforcesPlanLimits
{
    /**
     * Empresa objetivo de la creación: la elegida en el form (super_admin) o,
     * en su defecto, la del usuario autenticado (admin de empresa).
     */
    protected function planLimitCompany(): ?Company
    {
        /** @var array<string, mixed> $data */
        $data = $this->data;
        $companyId = $data['company_id'] ?? Auth::user()?->company_id;

        return $companyId ? Company::find((int) $companyId) : null;
    }

    /**
     * Serializa las creaciones concurrentes de la misma empresa (#169). Bloquea la
     * fila de la suscripción con `lockForUpdate`; como Filament ejecuta
     * `beforeCreate → handleRecordCreation → saveRelationships` en UNA transacción
     * (la página fija `$hasDatabaseTransactions = true`), el lock se mantiene hasta el
     * commit → un segundo request espera al primero y recién entonces cuenta y hace su
     * assert, viendo ya el registro nuevo. Sin transacción sería un no-op inofensivo.
     */
    protected function lockPlanForUpdate(Company $company): void
    {
        Subscription::query()
            ->where('company_id', $company->id)
            ->lockForUpdate()
            ->first();
    }

    protected function guardPlanLimit(Closure $assertion): void
    {
        try {
            $assertion();
        } catch (PlanLimitExceededException $e) {
            Notification::make()
                ->title('Límite del plan alcanzado')
                ->body($e->getMessage())
                ->danger()
                ->persistent()
                ->send();

            $this->halt(shouldRollbackDatabaseTransaction: true);
        }
    }
}
