<?php

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

use App\Models\Central\Tenant;
use App\Models\Lookups\PaymentMethod;
use App\Models\Sales\Transaction;
use App\Services\AuditLog;
use App\Services\CurrentContext;
use Filament\Actions\CreateAction;
use Filament\Actions\DeleteAction;
use Filament\Actions\EditAction;
use Filament\Forms\Components\Repeater;
use Filament\Forms\Components\Select;
use Filament\Forms\Components\TagsInput;
use Filament\Forms\Components\TextInput;
use Filament\Forms\Components\Toggle;
use Filament\Notifications\Notification;
use Filament\Resources\RelationManagers\RelationManager;
use Filament\Schemas\Schema;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Columns\ToggleColumn;
use Filament\Tables\Table;
use Illuminate\Support\Facades\Auth;
use Illuminate\Validation\Rule;

/**
 * Master-control toggle for which tender types a tenant accepts, and whether
 * a reference / cheque details are mandatory for each — the gap flagged
 * directly by the tenant ("no option to set what payment methods are
 * allowed... verification required / cheque fields required should be
 * captured").
 *
 * CASH/CARD/BANK_TRANSFER/CHEQUE are seeded per tenant by LookupSeeder and
 * can be toggled/tuned here but never deleted (their codes are load-bearing
 * throughout PaymentService, the cheque lifecycle commands, and the POS
 * checkout UI). Tenants can additionally define their own custom methods
 * with extra checkout fields, fully editable/deletable here.
 *
 * CREDIT and OTHER ({@see PaymentMethod::SYSTEM}) are internal bookkeeping
 * codes, never a real "how did the customer pay" choice — deliberately
 * excluded from this list so an operator can't accidentally disable them.
 */
class PaymentMethodsRelationManager extends RelationManager
{
    protected static string $relationship = 'paymentMethods';

    protected static ?string $title = 'Payment Methods';

    protected static ?string $recordTitleAttribute = 'name';

    /**
     * No per-model policy — landlord panel authorization is "authenticated
     * central admin, full stop" (matching every other relation manager here).
     */
    protected static bool $shouldSkipAuthorization = true;

    public function form(Schema $schema): Schema
    {
        return $schema->components([
            TextInput::make('name')
                ->required()
                ->maxLength(100),
            TextInput::make('code')
                ->required()
                ->maxLength(50)
                ->alphaDash()
                ->helperText('Fixed once saved — used internally to key checkout logic.')
                // Immutable after creation: the code is what POS/invoice/
                // transaction flows key off, so letting it change out from
                // under existing transactions would silently break history.
                ->disabled(fn (?PaymentMethod $record): bool => $record !== null)
                ->dehydrated(fn (?PaymentMethod $record): bool => $record === null)
                ->rule(fn (?PaymentMethod $record) => Rule::unique('payment_methods', 'code')
                    ->where('tenant_id', $this->getOwnerRecord()->getKey())
                    ->ignore($record?->id))
                ->rule(fn (?PaymentMethod $record) => $record === null
                    ? Rule::notIn(PaymentMethod::BUILT_IN)
                    : null)
                ->validationMessages([
                    'not_in' => 'That code is reserved for a built-in payment method.',
                ]),
            Toggle::make('is_active')
                ->label('Active — accepted at checkout')
                ->default(true),
            Toggle::make('requires_reference')
                ->label('Requires a reference / verification number')
                ->helperText('Cashier must enter a reference (e.g. transaction ID) before this method can be used.'),
            Toggle::make('requires_cheque_fields')
                ->label('Requires cheque details')
                ->helperText('Cashier must fill in cheque number, bank and date before this method can be used.'),
            Toggle::make('auto_approve')
                ->label('Auto-approve (skip pending review)')
                ->helperText('Off = payments wait as "pending" until manually approved, like cheques do by default.'),
            TextInput::make('sort_order')
                ->numeric()
                ->default(0)
                ->minValue(0),
            Repeater::make('fields')
                ->label('Extra checkout fields')
                ->helperText('Only used for custom (non built-in) methods — cash/card/bank transfer/cheque keep their own fixed fields.')
                ->schema([
                    TextInput::make('key')->required()->maxLength(50)->alphaDash(),
                    TextInput::make('label')->required()->maxLength(100),
                    Select::make('type')
                        ->options([
                            'text' => 'Text',
                            'number' => 'Number',
                            'dropdown' => 'Dropdown',
                            'file' => 'File upload',
                        ])
                        ->default('text')
                        ->required(),
                    Toggle::make('required')->default(false),
                    TagsInput::make('options')
                        ->label('Dropdown options')
                        ->visible(fn (callable $get): bool => $get('type') === 'dropdown'),
                ])
                ->columns(2)
                ->addActionLabel('Add field')
                ->visible(fn (?PaymentMethod $record): bool => $record === null || $record->isCustom())
                ->columnSpanFull(),
        ])->columns(2);
    }

    public function table(Table $table): Table
    {
        return $table
            // CREDIT/OTHER are internal bookkeeping codes, not a real checkout
            // choice — hidden here so they can never be toggled off.
            ->modifyQueryUsing(fn ($query) => $query->whereNotIn('code', PaymentMethod::SYSTEM))
            ->columns([
                TextColumn::make('name')->searchable()->sortable(),
                TextColumn::make('code')->badge()->color('gray'),
                ToggleColumn::make('is_active')
                    ->label('Active')
                    ->afterStateUpdated(fn (PaymentMethod $record) => static::auditMethod($record, 'payment_method.toggled', [
                        'is_active' => $record->is_active ? 'true' : 'false',
                    ])),
                ToggleColumn::make('requires_reference')->label('Ref. required'),
                ToggleColumn::make('requires_cheque_fields')->label('Cheque fields required'),
                TextColumn::make('sort_order')->label('Order')->sortable(),
            ])
            ->defaultSort('sort_order')
            ->headerActions([
                CreateAction::make()
                    ->after(fn (PaymentMethod $record) => static::auditMethod($record, 'payment_method.created')),
            ])
            ->recordActions([
                EditAction::make()
                    ->after(fn (PaymentMethod $record) => static::auditMethod($record, 'payment_method.updated')),
                DeleteAction::make()
                    // Built-ins are load-bearing throughout checkout logic and
                    // can never be removed, only toggled off.
                    ->visible(fn (PaymentMethod $record): bool => $record->isCustom())
                    ->action(function (PaymentMethod $record, DeleteAction $action): void {
                        if (Transaction::where('payment_method_id', $record->id)->exists()) {
                            Notification::make()
                                ->title('Cannot delete: this method has recorded transactions.')
                                ->body('Turn it off instead so history stays intact.')
                                ->danger()
                                ->send();

                            $action->cancel();

                            return;
                        }

                        $record->delete();
                        static::auditMethod($record, 'payment_method.deleted');

                        Notification::make()->title('Payment method deleted.')->success()->send();
                    }),
            ]);
    }

    /**
     * Landlord operations are written to the affected tenant's audit trail
     * with the central operator's identity in the context — mirroring
     * BranchesRelationManager::auditBranch().
     *
     * @param  array<string, mixed>  $context
     */
    protected static function auditMethod(PaymentMethod $method, string $action, array $context = []): void
    {
        $tenant = $method->tenant_id ? Tenant::query()->find($method->tenant_id) : null;

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

        $context['name'] = $method->name;
        $context['code'] = $method->code;
        $context['central_admin'] = Auth::guard('central')->user()?->email;

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