<?php

namespace App\Filament\Resources;

use App\Filament\Exports\ProductExporter;
use App\Filament\Resources\Concerns\HasModulePermissions;
use App\Filament\Resources\Concerns\HasStandardTableFeatures;
use App\Filament\Resources\PackagingProductResource\Pages\CreatePackagingProduct;
use App\Filament\Resources\PackagingProductResource\Pages\EditPackagingProduct;
use App\Filament\Resources\PackagingProductResource\Pages\ListPackagingProducts;
use App\Filament\Support\Chip;
use App\Filament\Support\RelationSelect;
use App\Models\Catalog\MainCategory;
use App\Models\Catalog\Product;
use App\Models\Catalog\SubCategory;
use App\Services\AuditLog;
use Filament\Actions\DeleteAction;
use Filament\Actions\EditAction;
use Filament\Forms\Components\Hidden;
use Filament\Forms\Components\Textarea;
use Filament\Forms\Components\TextInput;
use Filament\Forms\Components\Toggle;
use Filament\Resources\Resource;
use Filament\Schemas\Components\Section;
use Filament\Schemas\Schema;
use Filament\Support\Icons\Heroicon;
use Filament\Tables\Columns\IconColumn;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Enums\FiltersLayout;
use Filament\Tables\Filters\Filter;
use Filament\Tables\Filters\SelectFilter;
use Filament\Tables\Filters\TernaryFilter;
use Filament\Tables\Table;
use Illuminate\Database\Eloquent\Builder;

/**
 * A trimmed-down product screen for the "Packaging & Add-ons" system category
 * (bags, gift boxes, wrapping) — same {@see Product} model and the same
 * pricing/stock/SKU machinery as the main catalog (ProductResource), just:
 *
 *  - main_category_id is fixed to MainCategory::packaging() (a hidden field,
 *    never a user choice), so a packaging item can never be filed anywhere
 *    else and this resource's own query never has to guess what belongs here;
 *  - no image upload, no barcode field/column/print actions — these items
 *    aren't scanned or photographed for a catalog listing;
 *  - the Sub Category picker is scoped to packaging's own subcategories
 *    (Shopping Bags, Gift Boxes, ...) instead of depending on a live
 *    main-category selection, because there isn't one to depend on.
 *
 * Reuses ProductResource's permission module (isms_products) — this is the
 * same entity under a different, filtered screen, not a separate feature to
 * gate independently — and its Create/Edit pages by extending them (SKU
 * generation, attribute sync, audit logging all come along unchanged; only
 * $resource is overridden). See ProductResource's own docblock for the other
 * half of this split: it excludes packaging products end to end.
 */
class PackagingProductResource extends Resource
{
    use HasModulePermissions;
    use HasStandardTableFeatures;

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

    protected static string $permissionModuleKey = 'isms_products';

    protected static string $exporter = ProductExporter::class;

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

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

    protected static ?string $navigationLabel = 'Packaging & Add-ons';

    protected static ?int $navigationSort = 40;

    protected static ?string $slug = 'packaging-products';

    protected static ?string $modelLabel = 'packaging item';

    protected static ?string $pluralModelLabel = 'Packaging & Add-ons';

    public static function form(Schema $schema): Schema
    {
        return $schema->components([
            Section::make('Details')->schema([
                // dehydrateStateUsing, not default(): default() only seeds the
                // form the first time it mounts — a crafted request can still
                // set Livewire state directly. dehydrateStateUsing runs at save
                // time and OVERWRITES whatever is there, so this can never save
                // as anything but the packaging category, regardless of client
                // input (the same defense-in-depth reasoning ProductResource
                // applies to its own server-side-only form fields).
                Hidden::make('main_category_id')
                    ->dehydrateStateUsing(fn (): ?int => MainCategory::packaging()?->id)
                    ->dehydrated(),
                TextInput::make('name')->required()->maxLength(200)->columnSpanFull(),
                RelationSelect::make(
                    name: 'sub_category_id',
                    relationship: 'subCategory',
                    titleAttribute: 'name',
                    createPermission: 'isms_sub_categories.create',
                    modifyQueryUsing: fn ($query, $record) => $query
                        ->where('main_category_id', MainCategory::packaging()?->id)
                        ->where(fn ($q) => $q->where('is_active', true)->when($record?->sub_category_id, fn ($qq, $id) => $qq->orWhere('id', $id))),
                )
                    ->label('Type')
                    ->required(fn (): bool => MainCategory::packaging()?->subCategories()->exists() ?? false)
                    ->rule(function () {
                        return function (string $attribute, $value, \Closure $fail): void {
                            if (blank($value)) {
                                return;
                            }
                            $packagingId = MainCategory::packaging()?->id;
                            if ((int) SubCategory::whereKey($value)->value('main_category_id') !== (int) $packagingId) {
                                $fail('The selected type does not belong to Packaging & Add-ons.');
                            }
                        };
                    }),
                RelationSelect::make(
                    name: 'base_unit_id',
                    relationship: 'baseUnit',
                    titleAttribute: 'name',
                    modifyQueryUsing: fn ($query) => $query->where('is_active', true),
                )
                    ->label('Base Unit')
                    ->required(),
                Toggle::make('is_active')->default(true),
            ])->columns(2),

            // Price is a property of the receipt lot, not the catalog entry —
            // packaging products are ordinary Product rows priced per
            // consignment on the GRN, never on this form.
            Section::make('Description')->schema([
                Textarea::make('description')->columnSpanFull(),
            ]),
        ]);
    }

    public static function table(Table $table): Table
    {
        return static::standardTableDefaults($table)
            ->recordUrl(static::standardRecordUrl())
            ->emptyStateHeading('No packaging products yet')
            ->emptyStateDescription('Bags and packaging sold at the till will appear here.')
            ->columns([
                Chip::documentNumber('sku', 'SKU')->searchable()->sortable(),
                TextColumn::make('name')->searchable()->sortable(),
                TextColumn::make('subCategory.name')->label('Type')->searchable(),
                IconColumn::make('is_active')->boolean()->label('Active'),
            ])
            ->filters([
                Filter::make('name')
                    ->schema([TextInput::make('value')->label('Name')])
                    ->query(fn (Builder $query, array $data): Builder => $query->when(
                        $data['value'] ?? null,
                        fn (Builder $q, $value) => $q->where('name', 'like', "%{$value}%"),
                    )),
                SelectFilter::make('sub_category_id')
                    ->label('Type')
                    ->relationship('subCategory', 'name', fn ($query) => $query->where('main_category_id', MainCategory::packaging()?->id)),
                TernaryFilter::make('is_active')->label('Active'),
            ])
            ->filtersLayout(FiltersLayout::AboveContentCollapsible)
            ->filtersFormColumns(2)
            ->headerActions(static::standardExportActions())
            ->toolbarActions(static::standardExportBulkActions())
            ->defaultSort('is_active', 'desc')
            ->recordActions([
                EditAction::make(),
                DeleteAction::make()
                    ->after(fn (Product $record) => AuditLog::record('product.deleted', $record, [
                        'name' => $record->name,
                        'code' => $record->sku,
                    ])),
            ]);
    }

    public static function getPages(): array
    {
        return [
            'index' => ListPackagingProducts::route('/'),
            'create' => CreatePackagingProduct::route('/create'),
            'edit' => EditPackagingProduct::route('/{record}/edit'),
        ];
    }

    public static function getRelations(): array
    {
        return [
        ];
    }

    /**
     * The other half of ProductResource::getEloquentQuery()'s exclusion: this
     * resource shows ONLY packaging products, so every page (list, create,
     * edit, view) is fenced to them — a direct link to an ordinary product's
     * edit URL under this resource 404s rather than rendering it through a
     * form with no image/barcode fields.
     */
    public static function getEloquentQuery(): Builder
    {
        $packaging = MainCategory::packaging();

        return parent::getEloquentQuery()->when(
            $packaging,
            fn (Builder $query) => $query->where('main_category_id', $packaging->id),
            fn (Builder $query) => $query->whereRaw('1 = 0'),
        );
    }
}
