<?php

namespace App\Filament\Support;

use App\Models\Customers\Customer;
use App\Models\Geo\City;
use Filament\Actions\Action;
use Filament\Forms\Components\Select;
use Filament\Forms\Components\TextInput;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Facades\Auth;

/**
 * The customer picker used wherever an order is taken from a named customer.
 *
 * Differs from the generic {@see RelationSelect} in the two ways that matter at
 * an order desk: it searches by **phone number** as well as business name and
 * NIC (a customer on the phone gives their number, not their registered
 * business name), and the "+" create form asks only for what someone can
 * realistically capture mid-call — name, phone, NIC, email, city — leaving the
 * rest of the customer record to be filled in later from the Customers module.
 *
 * Option labels carry the phone inline so two similarly-named businesses are
 * distinguishable in the dropdown without opening either.
 */
class CustomerSelect
{
    /**
     * Everything {@see label()} needs, and nothing else. The picker only ever
     * renders a name and a phone, but this select is re-evaluated on every
     * render of the form it sits on — hydrating 50 full customer records
     * (addresses, rep assignments, audit columns) each time to print two
     * fields of each is work nobody asked for.
     */
    private const LABEL_COLUMNS = ['id', 'business_name', 'phone'];

    /**
     * @param  string  $name  the FK column on the owning form (usually `customer_id`)
     */
    public static function make(string $name = 'customer_id'): Select
    {
        return Select::make($name)
            ->label('Customer')
            ->searchable()
            ->preload()
            ->getSearchResultsUsing(fn (string $search): array => static::query()
                ->where(fn ($q) => $q
                    ->where('business_name', 'like', "%{$search}%")
                    ->orWhere('phone', 'like', "%{$search}%")
                    ->orWhere('nic', 'like', "%{$search}%"))
                ->orderBy('business_name')
                ->limit(50)
                ->get(static::LABEL_COLUMNS)
                ->mapWithKeys(fn (Customer $c): array => [$c->id => static::label($c)])
                ->all())
            ->getOptionLabelUsing(fn ($value): ?string => ($c = Customer::query()
                ->whereKey($value)
                ->first(static::LABEL_COLUMNS)) ? static::label($c) : null)
            ->options(fn (): array => static::query()
                ->orderBy('business_name')
                ->limit(50)
                ->get(static::LABEL_COLUMNS)
                ->mapWithKeys(fn (Customer $c): array => [$c->id => static::label($c)])
                ->all())
            ->searchPrompt('Search by phone number, name or NIC…')
            ->createOptionForm([
                TextInput::make('business_name')->label('Customer name')->required()->maxLength(200),
                TextInput::make('phone')->required()->maxLength(30)->tel(),
                TextInput::make('nic')->label('NIC')->maxLength(20),
                TextInput::make('email')->email()->maxLength(150),
                RelationSelect::standalone(
                    name: 'city_id',
                    modelClass: City::class,
                    titleAttribute: 'name',
                )->label('City'),
            ])
            ->createOptionUsing(fn (array $data): int => Customer::create($data + ['is_active' => true])->getKey())
            ->createOptionAction(
                fn (Action $action): Action => $action
                    ->label('New customer')
                    ->modalHeading('New customer')
                    ->modalSubmitActionLabel('Create')
                    ->visible(fn (): bool => Auth::user()?->hasPermissionTo('isms_customers.create') ?? false),
            );
    }

    /**
     * @return Builder<Customer>
     */
    private static function query()
    {
        return Customer::query()->where('is_active', true);
    }

    private static function label(Customer $customer): string
    {
        return $customer->phone
            ? "{$customer->business_name} — {$customer->phone}"
            : (string) $customer->business_name;
    }
}
