<?php

namespace App\Filament\Widgets;

use App\Models\Inventory\StockBatch;
use App\Models\Lookups\Status;
use App\Services\CurrentContext;
use App\Support\Quantity;
use Carbon\Carbon;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Table;
use Filament\Widgets\TableWidget;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Facades\Auth;

/**
 * The expiry alert table, mirroring ExpiryReport's bucketing
 * (expired / <=7 critical / <=30 warning): batches at or past their expiry
 * date always show, others only when they fall inside the 30-day window.
 */
class ExpiringStockWidget extends TableWidget
{
    protected static ?int $sort = 3;

    /** Days ahead of expiry flagged on the dashboard. */
    public const WINDOW_DAYS = 30;

    protected int|string|array $columnSpan = 'full';

    public static function canView(): bool
    {
        return Auth::user()?->hasPermissionTo('dashboard.access') ?? false;
    }

    public function table(Table $table): Table
    {
        $branchId = app(CurrentContext::class)->branchId();
        $today = Carbon::today();

        $ids = StockBatch::query()
            ->when($branchId, fn ($q) => $q->where('branch_id', $branchId))
            ->whereNotNull('expiry_date')
            ->where(fn ($q) => $q->where('available_quantity', '>', 0)->orWhere('reserved_quantity', '>', 0))
            ->whereHas('status', fn ($q) => $q->whereIn('code', [Status::BATCH_ACTIVE, Status::BATCH_EXPIRED]))
            ->fefoOrder()
            ->get(['id', 'expiry_date'])
            ->map(fn (StockBatch $b): array => [
                'id' => $b->id,
                'days_until' => (int) $today->diffInDays($b->expiry_date, false),
            ])
            // Already-expired batches always show, regardless of the window.
            ->filter(fn (array $r): bool => $r['days_until'] < 0 || $r['days_until'] <= self::WINDOW_DAYS)
            ->sortBy('days_until')
            ->take(15)
            ->pluck('id');

        return $table
            ->heading('Expiring stock')
            ->description('Batches expiring within '.self::WINDOW_DAYS.' days, soonest first.')
            ->query(
                fn (): Builder => StockBatch::query()
                    ->with(['product:id,name,sku,base_unit_id', 'product.baseUnit:id,code,name'])
                    ->whereIn('id', $ids)
                    ->orderBy('expiry_date')
            )
            ->paginated(false)
            ->columns([
                TextColumn::make('product.name')->label('Product')->placeholder('—'),
                TextColumn::make('product.sku')->label('SKU')->placeholder('—'),
                TextColumn::make('internal_batch_number')->label('Batch #')->badge()->color('gray'),
                TextColumn::make('expiry_date')->label('Expiry')->date('M d, Y'),
                TextColumn::make('days_until')
                    ->label('Days left')
                    ->formatStateUsing(fn (StockBatch $record): string => (function () use ($today, $record): string {
                        $days = (int) $today->diffInDays($record->expiry_date, false);

                        return $days < 0 ? 'Expired' : $days.'d';
                    })())
                    ->badge()
                    ->color(fn (StockBatch $record): string => (int) $today->diffInDays($record->expiry_date, false) <= 7 ? 'danger' : 'warning'),
                TextColumn::make('available_quantity')
                    ->label('Qty')
                    ->formatStateUsing(fn ($state, StockBatch $record): string => Quantity::format($state, $record->product?->baseUnit)),
                TextColumn::make('value_at_risk')
                    ->label('Value at risk')
                    ->state(fn (StockBatch $record): string => 'Rs. '.number_format((float) $record->available_quantity * (float) ($record->batch_cost ?? 0), 2)),
            ]);
    }
}
