<?php

namespace App\Filament\Resources;

use App\Filament\Resources\Concerns\HasModulePermissions;
use App\Filament\Resources\Concerns\HasStandardTableFeatures;
use App\Filament\Resources\StockBatchResource\Pages\ListStockBatches;
use App\Filament\Resources\StockBatchResource\Pages\ViewStockBatch;
use App\Filament\Support\Chip;
use App\Filament\Support\Perm;
use App\Filament\Support\StatusLookup;
use App\Models\Inventory\StockBatch;
use App\Models\Lookups\Status;
use App\Services\AuditLog;
use App\Services\BatchService;
use App\Support\Quantity;
use Filament\Actions\Action;
use Filament\Forms\Components\Textarea;
use Filament\Notifications\Notification;
use Filament\Resources\Resource;
use Filament\Schemas\Schema;
use Filament\Support\Icons\Heroicon;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Filters\SelectFilter;
use Filament\Tables\Table;

/**
 * Ported from App\Http\Controllers\Isms\StockBatchController
 * (docs/migration-plan.md Phase 4). Read-only + recall — batches are only
 * ever created via GRN confirmation (Phase 3's GrnService), matching the
 * source (no create/edit route here at all). Default sort replicates the
 * source's fefoOrder() scope: soonest-expiring first, nulls last.
 */
class StockBatchResource extends Resource
{
    use HasModulePermissions;
    use HasStandardTableFeatures;

    protected static ?string $model = StockBatch::class;

    protected static string $permissionModuleKey = 'inventory_batches';

    protected static string|\BackedEnum|null $navigationIcon = Heroicon::OutlinedCubeTransparent;

    protected static string|\UnitEnum|null $navigationGroup = 'Inventory';

    protected static ?string $navigationLabel = 'Stock Batches';

    protected static ?int $navigationSort = 40;

    public static function table(Table $table): Table
    {
        return static::standardTableDefaults($table)
            ->recordUrl(static::standardRecordUrl())
            ->emptyStateHeading('No stock batches yet')
            ->emptyStateDescription('Batches received against GRNs will appear here.')
            ->columns([
                Chip::documentNumber('internal_batch_number', 'Batch #')->searchable(),
                TextColumn::make('product.name')->searchable(),
                TextColumn::make('branch.name'),
                TextColumn::make('available_quantity')
                    ->formatStateUsing(fn ($state, StockBatch $record): string => Quantity::format($state, $record->product?->baseUnit)),
                TextColumn::make('expiry_date')->date()->placeholder('—'),
                TextColumn::make('status.name')
                    ->badge()
                    ->color(fn (StockBatch $record): string => StatusLookup::colorOfStatus($record->status)),
            ])
            ->modifyQueryUsing(fn ($query) => $query
                ->with('product.baseUnit')
                ->orderByRaw('CASE WHEN expiry_date IS NULL THEN 1 ELSE 0 END')
                ->orderBy('expiry_date'))
            ->defaultSort('created_at')
            ->filters([
                SelectFilter::make('product_id')->relationship('product', 'name'),
                SelectFilter::make('status_id')->label('Status')->options(fn () => StatusLookup::forContext(Status::CTX_STOCK_BATCH)->pluck('name', 'id')),
            ])
            ->recordActions([
                Action::make('view')
                    ->url(fn (StockBatch $record): string => ViewStockBatch::getUrl(['record' => $record]))
                    ->visible(fn (StockBatch $record): bool => static::canView($record)),
                // Active is the ONLY state a lot can be parked from. Allowing it
                // from 'recalled' would open an un-recall backdoor — recall has
                // no inverse action, but deactivate → reactivate would hand a
                // recalled lot back as active.
                Action::make('deactivate')
                    ->label('Deactivate')
                    ->icon(Heroicon::OutlinedPauseCircle)
                    ->color('warning')
                    ->visible(fn (StockBatch $record): bool => Perm::can('inventory_batches', 'deactivate') && $record->isActive())
                    ->requiresConfirmation()
                    ->modalHeading('Deactivate this batch?')
                    ->modalDescription('The batch keeps its stock but stops appearing at the POS, on sales orders, and in transfer, adjustment and write-off pickers. Reactivate it any time.')
                    ->schema([
                        Textarea::make('reason')->maxLength(500)->helperText('Optional — appended to the batch notes.'),
                    ])
                    ->action(function (StockBatch $record, array $data): void {
                        app(BatchService::class)->deactivate($record, $data['reason'] ?? null);
                        AuditLog::record('batch.deactivated', $record, [
                            'internal_batch_number' => $record->internal_batch_number,
                            'reason' => $data['reason'] ?? null,
                        ]);
                        Notification::make()->title('Batch deactivated.')->success()->send();
                    }),
                Action::make('reactivate')
                    ->label('Reactivate')
                    ->icon(Heroicon::OutlinedPlayCircle)
                    ->color('success')
                    ->visible(fn (StockBatch $record): bool => Perm::can('inventory_batches', 'deactivate') && $record->isInactive())
                    ->requiresConfirmation()
                    ->modalHeading('Reactivate this batch?')
                    ->modalDescription('The batch becomes available again everywhere lots are offered. A batch whose expiry passed while it was parked comes back as expired, not active.')
                    ->action(function (StockBatch $record): void {
                        app(BatchService::class)->reactivate($record);
                        AuditLog::record('batch.reactivated', $record->refresh(), [
                            'internal_batch_number' => $record->internal_batch_number,
                            'status' => $record->status?->name,
                        ]);
                        Notification::make()->title('Batch reactivated.')->success()->send();
                    }),
                Action::make('recall')
                    ->visible(fn (StockBatch $record): bool => Perm::can('inventory_batches', 'recall') && $record->status?->code !== Status::BATCH_RECALLED)
                    ->requiresConfirmation()
                    ->schema([
                        Textarea::make('reason')->required()->maxLength(500),
                    ])
                    ->action(function (StockBatch $record, array $data): void {
                        app(BatchService::class)->recall($record, $data['reason']);
                        AuditLog::record('batch.recalled', $record, [
                            'internal_batch_number' => $record->internal_batch_number,
                            'reason' => $data['reason'],
                            'description' => "Recalled batch {$record->internal_batch_number}: {$data['reason']}",
                        ]);
                        Notification::make()->title('Batch recalled.')->success()->send();
                    }),
            ]);
    }

    public static function form(Schema $schema): Schema
    {
        return $schema->components([]);
    }

    public static function getPages(): array
    {
        return [
            'index' => ListStockBatches::route('/'),
            'view' => ViewStockBatch::route('/{record}'),
        ];
    }
}
