<?php

declare(strict_types=1);

namespace App\Filament\Pages\Concerns;

use App\Filament\Support\DberpFormSaveSubheading;
use App\Models\DberpSystemProfile;
use App\Services\DberpAuditService;
use Filament\Actions\Action;
use Filament\Actions\ActionGroup;
use Filament\Notifications\Notification;
use Filament\Pages\Concerns\CanUseDatabaseTransactions;
use Filament\Schemas\Components\Actions;
use Filament\Schemas\Components\Component;
use Filament\Schemas\Components\EmbeddedSchema;
use Filament\Schemas\Components\Form;
use Filament\Schemas\Schema;
use Filament\Support\Exceptions\Halt;
use Illuminate\Contracts\Support\Htmlable;
use Throwable;

/**
 * 单例系统档案页：与 Filament EditTenantProfile 同构（表单 + 保存）。
 *
 * @property-read Schema $form
 */
trait ManagesDberpSystemProfileForm
{
    use CanUseDatabaseTransactions;
    use InteractsWithDberpNavigationGroupBreadcrumbs;

    /**
     * @var array<string, mixed>|null
     */
    public ?array $data = [];

    protected ?DberpSystemProfile $systemProfileRecord = null;

    /**
     * Livewire 子请求不会恢复 protected 模型引用，故在 mount / save / 表单绑定时统一解析。
     */
    protected function resolveSystemProfileRecord(): DberpSystemProfile
    {
        return $this->systemProfileRecord ??= DberpSystemProfile::query()->firstOrCreate(
            ['id' => 1],
            DberpSystemProfile::defaultAttributes(),
        );
    }

    public function mount(): void
    {
        $this->resolveSystemProfileRecord();

        $this->fillForm();
    }

    protected function fillForm(): void
    {
        $data = $this->resolveSystemProfileRecord()->attributesToArray();

        $this->callHook('beforeFill');

        $data = $this->mutateFormDataBeforeFill($data);

        $this->form->fill($data);

        $this->callHook('afterFill');
    }

    /**
     * @param  array<string, mixed>  $data
     * @return array<string, mixed>
     */
    protected function mutateFormDataBeforeFill(array $data): array
    {
        return $data;
    }

    /**
     * @param  array<string, mixed>  $data
     * @return array<string, mixed>
     */
    protected function mutateFormDataBeforeSave(array $data): array
    {
        return $data;
    }

    /**
     * @return list<string>
     */
    abstract protected function profileFieldNames(): array;

    public function getSubheading(): string|Htmlable|null
    {
        return DberpFormSaveSubheading::append(parent::getSubheading());
    }

    public function save(): void
    {
        try {
            $this->beginDatabaseTransaction();

            $profile = $this->resolveSystemProfileRecord();

            $this->callHook('beforeValidate');

            $data = $this->form->getState();

            $this->callHook('afterValidate');

            $data = $this->mutateFormDataBeforeSave($data);

            $this->callHook('beforeSave');

            $before = app(DberpAuditService::class)->snapshot($profile, $this->profileFieldNames());

            $profile->fill(collect($data)->only($this->profileFieldNames())->all());
            $profile->save();

            $after = app(DberpAuditService::class)->snapshot($profile->fresh(), $this->profileFieldNames());

            $this->logProfileSaved($before, $after);

            $this->callHook('afterSave');
        } catch (Halt $exception) {
            $exception->shouldRollbackDatabaseTransaction() ?
                $this->rollBackDatabaseTransaction() :
                $this->commitDatabaseTransaction();

            return;
        } catch (Throwable $exception) {
            $this->rollBackDatabaseTransaction();

            throw $exception;
        }

        $this->commitDatabaseTransaction();

        $this->getSavedNotification()?->send();
    }

    /**
     * @param  array<string, mixed>  $before
     * @param  array<string, mixed>  $after
     */
    protected function logProfileSaved(array $before, array $after): void
    {
        app(DberpAuditService::class)->log(
            $this->profileAuditAction(),
            $this->systemProfileRecord,
            $before,
            $after,
        );
    }

    abstract protected function profileAuditAction(): string;

    protected function getSavedNotification(): ?Notification
    {
        return Notification::make()
            ->success()
            ->title(__('dberp.notifications.saved'));
    }

    public function defaultForm(Schema $schema): Schema
    {
        return $schema
            ->operation('edit')
            ->model($this->resolveSystemProfileRecord())
            ->statePath('data');
    }

    abstract public function form(Schema $schema): Schema;

    /**
     * @return array<Action|ActionGroup>
     */
    protected function getFormActions(): array
    {
        return [
            $this->getSaveFormAction(),
        ];
    }

    protected function getSaveFormAction(): Action
    {
        return Action::make('save')
            ->label(__('dberp.form.save'))
            ->submit('save')
            ->keyBindings(['mod+s']);
    }

    public function content(Schema $schema): Schema
    {
        return $schema
            ->components([
                $this->getFormContentComponent(),
            ]);
    }

    public function getFormContentComponent(): Component
    {
        return Form::make([EmbeddedSchema::make('form')])
            ->id('form')
            ->livewireSubmitHandler('save')
            ->footer([
                Actions::make($this->getFormActions())
                    ->alignment($this->getFormActionsAlignment())
                    ->fullWidth($this->hasFullWidthFormActions())
                    ->sticky($this->areFormActionsSticky())
                    ->key('form-actions'),
            ]);
    }

    protected function hasFullWidthFormActions(): bool
    {
        return false;
    }
}
