<?php

namespace App\Filament\Resources;

use App\Filament\Resources\Concerns\AssertsBranchAccess;
use App\Filament\Resources\Concerns\HasModulePermissions;
use App\Filament\Resources\Concerns\HasStandardTableFeatures;
use App\Filament\Resources\ProductBundleResource\Pages\CreateProductBundle;
use App\Filament\Resources\ProductBundleResource\Pages\EditProductBundle;
use App\Filament\Resources\ProductBundleResource\Pages\ListProductBundles;
use App\Filament\Support\Chip;
use App\Filament\Support\ProductSelect;
use App\Filament\Support\RelationSelect;
use App\Models\Catalog\Product;
use App\Models\Catalog\ProductBundle;
use App\Services\AuditLog;
use App\Services\BatchService;
use App\Services\CurrentContext;
use App\Support\Quantity;
use Filament\Actions\DeleteAction;
use Filament\Actions\EditAction;
use Filament\Forms\Components\DatePicker;
use Filament\Forms\Components\Repeater;
use Filament\Forms\Components\Textarea;
use Filament\Forms\Components\TextInput;
use Filament\Forms\Components\Toggle;
use Filament\Infolists\Components\TextEntry;
use Filament\Resources\Resource;
use Filament\Schemas\Components\Section;
use Filament\Schemas\Components\Utilities\Get;
use Filament\Schemas\Schema;
use Filament\Support\Enums\FontWeight;
use Filament\Support\Icons\Heroicon;
use Filament\Tables\Columns\IconColumn;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Filters\TernaryFilter;
use Filament\Tables\Table;
use Illuminate\Support\Str;

/**
 * Ported from App\Http\Controllers\Isms\ProductBundleController
 * (docs/migration-plan.md Phase 2). Preserved behaviors:
 *  - sku auto-generated ("BDL-" + 8 random uppercase chars) when left blank
 *    on create, exactly as the source controller's store().
 *  - branch_id is optional: null = globally available bundle, set = only
 *    available at that branch (ProductBundle::scopeAvailableAtBranch()).
 *    No inline "+ create branch" here — matches the source app, where
 *    branch creation is central-panel-only (see BranchResource docblock).
 *  - items relationship is delete+reinsert on every save (source
 *    controller's update() does `$bundle->items()->delete()` then
 *    recreates), not a diff — Repeater::relationship() with
 *    saveRelationshipsUsing left to Filament's default (which does exactly
 *    this: delete removed rows, create new ones) reproduces that.
 *  - ends_at must be on/after starts_at.
 */
class ProductBundleResource extends Resource
{
    use AssertsBranchAccess;
    use HasModulePermissions;
    use HasStandardTableFeatures;

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

    protected static string $permissionModuleKey = 'isms_bundles';

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

    protected static string|\UnitEnum|null $navigationGroup = 'Promotions & Loyalty';

    protected static ?string $navigationLabel = 'Bundles';

    protected static ?int $navigationSort = 30;

    public static function form(Schema $schema): Schema
    {
        return $schema->components([
            Section::make('Details')->schema([
                TextInput::make('name')->required()->maxLength(150)->columnSpanFull(),
                TextInput::make('sku')
                    ->label('SKU')
                    ->maxLength(60)
                    ->helperText('Leave blank to auto-generate.'),
                TextInput::make('sale_price')
                    ->label('Bundle Selling Price')
                    ->required()->numeric()->minValue(0)->prefix('Rs.')
                    // Live comparison against the sum of the component prices
                    // (Normal Total Rate) so the discount being granted is
                    // visible at the exact spot the price is set.
                    ->helperText(function (Get $get): string {
                        $normal = static::itemsNormalTotal(
                            $get('items'),
                            $get('branch_id') ? (int) $get('branch_id') : null,
                        );

                        return $normal > 0
                            ? 'Normal Total Rate: Rs. '.number_format($normal, 2).' — bundle price saves Rs. '.number_format(max(0, $normal - (float) $get('sale_price')), 2)
                            : 'Normal Total Rate: Rs. 0.00';
                    }),
                RelationSelect::make(
                    name: 'branch_id',
                    relationship: 'branch',
                    titleAttribute: 'name',
                    modifyQueryUsing: fn ($query) => $query->where('is_active', true),
                )
                    ->label('Branch (blank = all branches)'),
                DatePicker::make('starts_at'),
                DatePicker::make('ends_at')
                    ->rule(function (Get $get) {
                        return function (string $attribute, $value, \Closure $fail) use ($get): void {
                            if (blank($value) || blank($get('starts_at'))) {
                                return;
                            }
                            if ($value < $get('starts_at')) {
                                $fail('End date must be on or after the start date.');
                            }
                        };
                    }),
                Toggle::make('is_active')->default(true),
                Textarea::make('description')->maxLength(1000)->columnSpanFull(),
            ]),

            Section::make('Items')->schema([
                Repeater::make('items')
                    ->relationship('items')
                    ->schema([
                        ProductSelect::make(
                            modifyQueryUsing: fn ($query) => $query->where('is_active', true),
                        )
                            ->label('Product')
                            ->required()
                            ->live()
                            ->columnSpan(2),
                        // Read-only pricing columns, re-derived on every render
                        // (never persisted): the component's FEFO-front
                        // sellable lot price at the bundle's branch (or the
                        // current context branch when the bundle is global).
                        TextEntry::make('unit_price')
                            ->label('Current Selling Price')
                            ->getStateUsing(fn (Get $get): float => static::componentPrice(
                                (int) ($get('product_id') ?? 0),
                                $get('../../branch_id') ? (int) $get('../../branch_id') : null,
                            ))
                            ->money(fn () => app(CurrentContext::class)->currencyCode()),
                        TextInput::make('quantity')
                            ->required()->numeric()->minValue(0.0001)->step(0.0001)
                            ->live()
                            ->suffix(fn (Get $get): ?string => Quantity::unitLabel(Product::find($get('product_id'))?->baseUnit)),
                        TextEntry::make('subtotal')
                            ->label('Subtotal (Rate Ã— Qty)')
                            ->getStateUsing(fn (Get $get): float => round(
                                static::componentPrice(
                                    (int) ($get('product_id') ?? 0),
                                    $get('../../branch_id') ? (int) $get('../../branch_id') : null,
                                ) * (float) ($get('quantity') ?? 0),
                                2,
                            ))
                            ->money(fn () => app(CurrentContext::class)->currencyCode()),
                    ])
                    ->columns(5)
                    ->minItems(1)
                    ->addActionLabel('Add item')
                    ->columnSpanFull(),
                // The dynamically-updated Normal Total Rate — the sum of every
                // component's own lot price Ã— its bundled quantity.
                TextEntry::make('normal_total')
                    ->label('Normal Total Rate')
                    ->getStateUsing(fn (Get $get): float => static::itemsNormalTotal(
                        $get('items'),
                        $get('branch_id') ? (int) $get('branch_id') : null,
                    ))
                    ->money(fn () => app(CurrentContext::class)->currencyCode())
                    ->weight(FontWeight::Bold)
                    ->columnSpanFull(),
            ]),
        ]);
    }

    /**
     * The FEFO-front sellable lot's price for a component at the given branch
     * (falling back to the current context branch, then 0). A component with
     * no sellable lot prices at 0 — the bundle form is a draft surface; the
     * sale-time expansion is what refuses an unpricable bundle.
     */
    private static function componentPrice(int $productId, ?int $branchId): float
    {
        $branchId ??= app(CurrentContext::class)->branchId();

        if (! $branchId) {
            return 0.0;
        }

        $lot = app(BatchService::class)->sellableFor($productId, $branchId)->first();

        return $lot?->sale_price !== null ? (float) $lot->sale_price : 0.0;
    }

    /**
     * Sum of each component's FEFO-front lot price Ã— its bundled quantity,
     * across all repeater rows — the "Normal Total Rate" the bundle's own
     * selling price is compared against.
     *
     * @param  mixed  $items  raw repeater state ($get('items'))
     */
    private static function itemsNormalTotal(mixed $items, ?int $branchId): float
    {
        $rows = collect($items ?? [])->filter(fn (array $row): bool => filled($row['product_id'] ?? null));

        if ($rows->isEmpty()) {
            return 0.0;
        }

        return (float) $rows->sum(fn (array $row): float => static::componentPrice((int) $row['product_id'], $branchId) * (float) ($row['quantity'] ?? 0));
    }

    public static function table(Table $table): Table
    {
        return static::standardTableDefaults($table)
            ->recordUrl(static::standardRecordUrl())
            ->emptyStateHeading('No bundles yet')
            ->emptyStateDescription('Bundles combine products into one sellable unit.')
            ->columns([
                Chip::documentNumber('sku', 'SKU')->searchable()->sortable(),
                TextColumn::make('name')->searchable()->sortable(),
                TextColumn::make('branch.name')->label('Branch')->placeholder('All branches'),
                TextColumn::make('sale_price')->money(fn () => app(CurrentContext::class)->currencyCode())->sortable(),
                TextColumn::make('items_count')->label('Items')->counts('items'),
                IconColumn::make('is_active')->boolean()->label('Active'),
            ])
            ->filters([
                TernaryFilter::make('is_active')->label('Active'),
            ])
            ->defaultSort('name')
            ->recordActions([
                EditAction::make(),
                DeleteAction::make()
                    ->after(fn (ProductBundle $record) => AuditLog::record('product_bundle.deleted', $record, [
                        'name' => $record->name,
                    ])),
            ]);
    }

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

    public static function generateSku(): string
    {
        return 'BDL-'.Str::upper(Str::random(8));
    }
}
