<?php

namespace App\Filament\Resources\UserResource\Pages;

use App\Filament\Forms\Components\PermissionOverridePicker;
use App\Filament\Resources\UserResource;
use App\Models\User;
use App\Services\AuditLog;
use Filament\Resources\Pages\EditRecord;
use Illuminate\Support\Facades\Auth;

class EditUser extends EditRecord
{
    protected static string $resource = UserResource::class;

    /** @var array<int, int> */
    protected array $roleIds = [];

    /** @var array{grant: array<int, string>, revoke: array<int, string>} */
    protected array $overrides = ['grant' => [], 'revoke' => []];

    /** @var array<string, mixed> */
    protected array $originalFields = [];

    /** @var array<int, string> */
    protected array $oldRoleNames = [];

    /** @var array<int, int> */
    protected array $oldTillIds = [];

    protected function mutateFormDataBeforeFill(array $data): array
    {
        /** @var User $user */
        $user = $this->record;

        $data['role_ids'] = $user->roles()->pluck('roles.id')->all();
        // The matrix edits the exception layer itself, so it loads the stored
        // overrides verbatim — not the effective set. Everything absent from
        // both lists is shown as inherited from the roles above.
        $data['permission_overrides'] = $user->permissionOverrideLists();

        return $data;
    }

    protected function mutateFormDataBeforeSave(array $data): array
    {
        /** @var User $user */
        $user = $this->record;

        // Captured now, before fill()+save() overwrites the in-memory model —
        // matches the controller's $original/$oldRoles/$oldTillIds, taken
        // before any of this request's changes are applied.
        $this->originalFields = $user->only(['name', 'email', 'phone', 'status']);
        $this->oldRoleNames = $user->roles()->pluck('name')->all();
        $this->oldTillIds = $user->tills()->pluck('tills.id')->sort()->values()->all();

        $this->roleIds = array_values(array_unique(array_map('intval', $data['role_ids'] ?? [])));
        // normalize() absorbs the shapes Livewire can send for an empty checkbox
        // group (`false`, `null`) so this typed property is always the two-list
        // array the rest of the flow expects.
        $this->overrides = PermissionOverridePicker::normalize($data['permission_overrides'] ?? null);
        unset($data['role_ids'], $data['permission_overrides']);

        $data['role_id'] = $this->roleIds[0] ?? null;
        $data['updated_by'] = Auth::id();

        if (! filled($data['password'] ?? null)) {
            unset($data['password']);
        }

        return $data;
    }

    protected function afterSave(): void
    {
        /** @var User $user */
        $user = $this->record;

        $overrides = UserResource::applyRolesAndOverrides($user, $this->roleIds, $this->overrides);

        $changes = [];
        foreach ($this->originalFields as $field => $oldValue) {
            $newValue = $user->{$field};
            if ($oldValue !== $newValue) {
                $changes[$field] = ['from' => $oldValue, 'to' => $newValue];
            }
        }

        $newRoleNames = $user->roles()->pluck('name')->all();
        if ($this->oldRoleNames !== $newRoleNames) {
            $changes['roles'] = ['from' => $this->oldRoleNames, 'to' => $newRoleNames];
        }

        $newTillIds = $user->tills()->pluck('tills.id')->sort()->values()->all();
        if ($this->oldTillIds !== $newTillIds) {
            $changes['till_ids'] = ['from' => $this->oldTillIds, 'to' => $newTillIds];
            AuditLog::record('till_access.updated', $user, ['from' => $this->oldTillIds, 'to' => $newTillIds]);
        }

        // An all-branch / full-admin user must never carry per-branch grants;
        // the warehouses picker is hidden for them, so clear anything a stale
        // form submission might have synced anyway.
        if ($user->is_all_branches || $user->isFullAdmin()) {
            $user->warehouses()->sync([]);
        }

        AuditLog::record('user.updated', $user, [
            'changes' => $changes,
            'allow_overrides' => $overrides['allow'],
            'deny_overrides' => $overrides['deny'],
        ]);
    }
}
