<?php

namespace App\Filament\Central\Resources\TenantResource\RelationManagers;

use App\Models\Branch;
use App\Models\Central\Tenant;
use App\Models\Sales\Till;
use App\Models\User;
use App\Services\AuditLog;
use App\Services\CurrentContext;
use App\Services\DocumentNumberService;
use Filament\Actions\CreateAction;
use Filament\Actions\DeleteAction;
use Filament\Actions\EditAction;
use Filament\Forms\Components\Select;
use Filament\Forms\Components\TextInput;
use Filament\Forms\Components\Toggle;
use Filament\Resources\RelationManagers\RelationManager;
use Filament\Schemas\Schema;
use Filament\Tables\Columns\IconColumn;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Table;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;

/**
 * Till management for a tenant, from the landlord panel.
 *
 * Like BranchesRelationManager this is a REBUILD: `Central\TillManagementController`
 * has the same fatal defect (`use App\Models\Tenant;` + `$tenant->run()`), so
 * central till CRUD has never worked either — a finding that extends Decisions
 * Log #15 beyond branches. Its validation rules are honoured exactly.
 */
class TillsRelationManager extends RelationManager
{
    protected static string $relationship = 'tills';

    protected static ?string $title = 'Tills';

    protected static ?string $recordTitleAttribute = 'name';

    /** Landlord panel — no per-model policy, see BranchesRelationManager. */
    protected static bool $shouldSkipAuthorization = true;

    public function form(Schema $schema): Schema
    {
        return $schema->components([
            TextInput::make('identifier')
                ->label('Identifier')
                ->disabled()
                ->dehydrated(false)
                ->placeholder('Generated automatically')
                ->helperText('Assigned automatically when the till is created.'),
            TextInput::make('name')->required()->maxLength(100),
            Select::make('branch_id')
                ->label('Branch')
                // Only this tenant's ACTIVE branches — the picker must never
                // leak another tenant's rows into a landlord screen, and an
                // inactive branch must not accept new tills.
                ->options(fn (): array => Branch::query()
                    ->where('tenant_id', $this->getOwnerRecord()->getKey())
                    ->where('is_active', true)
                    ->orderBy('name')
                    ->pluck('name', 'id')
                    ->all())
                ->helperText('Only active branches are listed.')
                ->searchable(),
            Toggle::make('is_active')->default(true),
        ])->columns(2);
    }

    public function table(Table $table): Table
    {
        return $table
            ->columns([
                TextColumn::make('identifier')->searchable()->sortable(),
                TextColumn::make('name')->searchable(),
                TextColumn::make('branch.name')->label('Branch')->placeholder('—'),
                IconColumn::make('is_active')->boolean(),
            ])
            ->defaultSort('name')
            ->headerActions([
                CreateAction::make()
                    // Central has no tenant bound to the request (landlord
                    // panel, `central` guard) — DocumentNumberService's
                    // per-tenant counter needs one explicitly, same idiom as
                    // every other cross-tenant write on this resource.
                    ->mutateFormDataUsing(function (array $data): array {
                        $data['identifier'] = app(CurrentContext::class)->runForTenant(
                            $this->getOwnerRecord(),
                            fn () => app(DocumentNumberService::class)->next('till'),
                        );

                        return $data;
                    })
                    ->after(function (Till $record): void {
                        static::auditTill($record, 'till.created');
                        static::assignTillToBranchUsers($record);
                    }),
            ])
            ->recordActions([
                EditAction::make()
                    ->after(fn (Till $record) => static::auditTill($record, 'till.updated')),
                // The source imposes no guard on till deletion, unlike branches.
                DeleteAction::make()
                    ->after(fn (Till $record) => static::auditTill($record, 'till.deleted')),
            ]);
    }

    /**
     * Landlord till operations land in the tenant's audit trail with the
     * central operator's identity in the context (actor_id stays null — they
     * are not a tenant user), mirroring TenantResource::audit().
     */
    protected static function auditTill(Till $till, string $action): void
    {
        $tenant = $till->tenant_id ? Tenant::query()->find($till->tenant_id) : null;

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

        $context = [
            'name' => $till->name,
            'identifier' => $till->identifier,
            'branch' => $till->branch?->name,
            'central_admin' => Auth::guard('central')->user()?->email,
        ];

        app(CurrentContext::class)->runForTenant($tenant, fn () => AuditLog::record($action, $till, $context));
    }

    /**
     * Assign a master-created till to every active tenant user who can already
     * access its branch (or who is unrestricted). This makes the till appear
     * immediately in both the tenant's Till master and POS, without a manual
     * `user_till_access` step, while still preserving per-cashier restriction
     * for users who already have explicit till assignments.
     */
    protected static function assignTillToBranchUsers(Till $till): void
    {
        $tenant = $till->tenant_id ? Tenant::query()->find($till->tenant_id) : null;
        if ($tenant === null || $till->branch_id === null) {
            return;
        }

        $branchId = (int) $till->branch_id;

        app(CurrentContext::class)->runForTenant($tenant, function () use ($till, $branchId): void {
            $users = User::query()
                ->where('tenant_id', $till->tenant_id)
                ->where('status', User::STATUS_ACTIVE)
                ->get(['id', 'is_all_branches']);

            foreach ($users as $user) {
                // Full admins and explicit all-branch users see every till (null)
                // — no need to write a pivot row, but writing one is harmless.
                // For branch-restricted users, only assign if they already have
                // this branch in their access list.
                if ($user->isFullAdmin() || $user->hasAllBranchAccess()) {
                    DB::table('user_till_access')->updateOrInsert(
                        ['user_id' => $user->id, 'till_id' => $till->id],
                        []
                    );
                    continue;
                }

                $hasBranch = DB::table('user_warehouse_access')
                    ->where('user_id', $user->id)
                    ->where('warehouse_id', $branchId)
                    ->exists();

                // Also include users with no explicit restriction yet (empty till
                // access is now permissive, so they would already see the till in
                // POS/master — but writing the row makes their future restriction
                // explicit and avoids a later "empty => all" surprise).
                $hasAnyTill = DB::table('user_till_access')->where('user_id', $user->id)->exists();
                $shouldAssign = $hasBranch || ! $hasAnyTill;

                if ($shouldAssign) {
                    DB::table('user_till_access')->updateOrInsert(
                        ['user_id' => $user->id, 'till_id' => $till->id],
                        []
                    );
                }
            }
        });
    }
}
