<?php

declare(strict_types=1);

/**
 * 本文件属于 DBErp 进销存系统。
 *
 * 版权所有 © 2026 北京珑大钜商科技有限公司。保留所有权利。
 *
 * 未经官方授权，不得删除、篡改或隐藏本文件中的版权声明、产品标识与来源信息。
 * 商业授权、官方更新与技术支持服务，请以珑大官方支持中心记录为准。
 *
 * @link https://support.loongdom.cn
 * @see  https://www.dberp.com.cn
 */

namespace App\Support\EntityNavigation;

use App\Filament\Resources\DberpCustomerReceivables\DberpCustomerReceivableResource;
use App\Filament\Resources\DberpCustomers\DberpCustomerResource;
use App\Filament\Resources\DberpInventoryBalances\DberpInventoryBalanceResource;
use App\Filament\Resources\DberpProducts\DberpProductResource;
use App\Filament\Resources\DberpPurchaseOrders\DberpPurchaseOrderResource;
use App\Filament\Resources\DberpSalesOrders\DberpSalesOrderResource;
use App\Filament\Resources\DberpSupplierPayables\DberpSupplierPayableResource;
use App\Filament\Resources\DberpSuppliers\DberpSupplierResource;
use App\Filament\Resources\DberpWarehouses\DberpWarehouseResource;
use App\Models\DberpCustomer;
use App\Models\DberpPayableRecord;
use App\Models\DberpProduct;
use App\Models\DberpPurchaseOrder;
use App\Models\DberpReceivableRecord;
use App\Models\DberpSalesOrder;
use App\Models\DberpSupplier;
use App\Models\DberpWarehouse;
use App\Models\User;
use App\Services\DberpPermissionService;
use App\Tools\Entity\EntityReference;
use App\Tools\Entity\EntityType;
use Illuminate\Database\Eloquent\Model;
use Throwable;

/**
 * 把已批准 EntityReference 转成当前用户可访问的只读 NavigationTarget。
 * 导航不是授权：每次重新检查权限；缺失/无权限/无路由时静默跳过。
 */
final class EntityNavigationResolver
{
    public function __construct(
        private readonly DberpPermissionService $permissions,
    ) {}

    /**
     * @return list<NavigationTarget>
     */
    public function resolve(User $user, EntityReference $reference): array
    {
        if (! $this->recordExists($reference)) {
            return [];
        }

        $targets = [];
        $open = $this->openTarget($user, $reference);

        if ($open instanceof NavigationTarget) {
            $targets[] = $open;
        }

        foreach ($this->relatedTargets($user, $reference) as $related) {
            $targets[] = $related;
        }

        return $targets;
    }

    /**
     * @param  list<EntityReference>  $references
     * @return list<NavigationTarget>
     */
    public function resolveMany(User $user, array $references): array
    {
        $unique = [];

        foreach ($references as $reference) {
            foreach ($this->resolve($user, $reference) as $target) {
                $unique[$target->key()] ??= $target;
            }
        }

        return array_values($unique);
    }

    private function openTarget(User $user, EntityReference $reference): ?NavigationTarget
    {
        $permission = $this->viewPermission($reference->entityType);

        if ($permission === null || ! $this->permissions->allows($user, $permission)) {
            return null;
        }

        $resource = $this->resourceClass($reference->entityType);

        if ($resource === null) {
            return null;
        }

        if (SafeFilamentResourceUrl::hasViewPage($resource)) {
            $url = SafeFilamentResourceUrl::page($resource, 'view', ['record' => $reference->entityId]);

            if ($url !== null) {
                return $this->target(
                    NavigationAction::Open,
                    NavigationKind::View,
                    $reference,
                    $this->actionLabel(NavigationAction::Open, $reference),
                    $url,
                );
            }
        }

        $url = $this->listFallbackUrl($resource, $reference);

        if ($url === null) {
            return null;
        }

        return $this->target(
            NavigationAction::Open,
            NavigationKind::FilteredList,
            $reference,
            $this->actionLabel(NavigationAction::Open, $reference),
            $url,
        );
    }

    /**
     * @return list<NavigationTarget>
     */
    private function relatedTargets(User $user, EntityReference $reference): array
    {
        return match ($reference->entityType) {
            EntityType::Product => array_values(array_filter([
                $this->relatedList(
                    $user,
                    $reference,
                    NavigationAction::ViewSalesOrders,
                    DberpPermissionService::VIEW_SALES_ORDER,
                    DberpSalesOrderResource::class,
                    ['product_id' => ['product_id' => $reference->entityId]],
                ),
                $this->relatedList(
                    $user,
                    $reference,
                    NavigationAction::ViewInventory,
                    DberpPermissionService::VIEW_INVENTORY_BALANCE,
                    DberpInventoryBalanceResource::class,
                    ['product_id' => ['value' => $reference->entityId]],
                ),
            ])),
            EntityType::Customer => array_values(array_filter([
                $this->relatedList(
                    $user,
                    $reference,
                    NavigationAction::ViewSalesOrders,
                    DberpPermissionService::VIEW_SALES_ORDER,
                    DberpSalesOrderResource::class,
                    ['customer_id' => ['value' => $reference->entityId]],
                ),
                $this->relatedList(
                    $user,
                    $reference,
                    NavigationAction::ViewReceivables,
                    DberpPermissionService::VIEW_CUSTOMER_RECEIVABLE,
                    DberpCustomerReceivableResource::class,
                    ['customer_id' => ['value' => $reference->entityId]],
                ),
            ])),
            EntityType::Supplier => array_values(array_filter([
                $this->relatedList(
                    $user,
                    $reference,
                    NavigationAction::ViewPurchaseOrders,
                    DberpPermissionService::VIEW_PURCHASE_ORDER,
                    DberpPurchaseOrderResource::class,
                    ['supplier_id' => ['value' => $reference->entityId]],
                ),
                $this->relatedList(
                    $user,
                    $reference,
                    NavigationAction::ViewPayables,
                    DberpPermissionService::VIEW_SUPPLIER_PAYABLE,
                    DberpSupplierPayableResource::class,
                    ['supplier_id' => ['value' => $reference->entityId]],
                ),
            ])),
            EntityType::Warehouse => array_values(array_filter([
                $this->relatedList(
                    $user,
                    $reference,
                    NavigationAction::ViewInventory,
                    DberpPermissionService::VIEW_INVENTORY_BALANCE,
                    DberpInventoryBalanceResource::class,
                    ['warehouse_id' => ['value' => $reference->entityId]],
                ),
            ])),
            default => [],
        };
    }

    /**
     * @param  class-string  $resourceClass
     * @param  array<string, mixed>  $filters
     */
    private function relatedList(
        User $user,
        EntityReference $reference,
        NavigationAction $action,
        string $permission,
        string $resourceClass,
        array $filters,
    ): ?NavigationTarget {
        if (! $this->permissions->allows($user, $permission)) {
            return null;
        }

        $url = SafeFilamentResourceUrl::filteredIndex($resourceClass, $filters);

        if ($url === null) {
            return null;
        }

        return $this->target(
            $action,
            NavigationKind::FilteredList,
            $reference,
            $this->actionLabel($action, $reference),
            $url,
        );
    }

    /**
     * @param  class-string  $resourceClass
     */
    private function listFallbackUrl(string $resourceClass, EntityReference $reference): ?string
    {
        $record = $this->findRecord($reference);
        $search = $this->searchTerm($record);

        if ($search !== null) {
            $url = SafeFilamentResourceUrl::searchedIndex($resourceClass, $search);

            if ($url !== null) {
                return $url;
            }
        }

        return SafeFilamentResourceUrl::filteredIndex($resourceClass);
    }

    private function recordExists(EntityReference $reference): bool
    {
        return $this->findRecord($reference) instanceof Model;
    }

    private function findRecord(EntityReference $reference): ?Model
    {
        $modelClass = $this->modelClass($reference->entityType);

        if ($modelClass === null) {
            return null;
        }

        try {
            $record = $modelClass::query()->whereKey($reference->entityId)->first();
        } catch (Throwable) {
            return null;
        }

        return $record instanceof Model ? $record : null;
    }

    private function searchTerm(?Model $record): ?string
    {
        if (! $record instanceof Model) {
            return null;
        }

        foreach (['code', 'name'] as $attribute) {
            $value = trim((string) ($record->getAttribute($attribute) ?? ''));

            if ($value !== '') {
                return $value;
            }
        }

        return null;
    }

    /**
     * @return class-string<Model>|null
     */
    private function modelClass(EntityType $type): ?string
    {
        return match ($type) {
            EntityType::Product => DberpProduct::class,
            EntityType::Customer => DberpCustomer::class,
            EntityType::Supplier => DberpSupplier::class,
            EntityType::Warehouse => DberpWarehouse::class,
            EntityType::SalesOrder => DberpSalesOrder::class,
            EntityType::PurchaseOrder => DberpPurchaseOrder::class,
            EntityType::Receivable => DberpReceivableRecord::class,
            EntityType::Payable => DberpPayableRecord::class,
        };
    }

    /**
     * @return class-string|null
     */
    private function resourceClass(EntityType $type): ?string
    {
        return match ($type) {
            EntityType::Product => DberpProductResource::class,
            EntityType::Customer => DberpCustomerResource::class,
            EntityType::Supplier => DberpSupplierResource::class,
            EntityType::Warehouse => DberpWarehouseResource::class,
            EntityType::SalesOrder => DberpSalesOrderResource::class,
            EntityType::PurchaseOrder => DberpPurchaseOrderResource::class,
            EntityType::Receivable => DberpCustomerReceivableResource::class,
            EntityType::Payable => DberpSupplierPayableResource::class,
        };
    }

    private function viewPermission(EntityType $type): ?string
    {
        return match ($type) {
            EntityType::Product => DberpPermissionService::VIEW_PRODUCT,
            EntityType::Customer => DberpPermissionService::VIEW_MASTER_CUSTOMER,
            EntityType::Supplier => DberpPermissionService::VIEW_MASTER_SUPPLIER,
            EntityType::Warehouse => DberpPermissionService::VIEW_WAREHOUSE,
            EntityType::SalesOrder => DberpPermissionService::VIEW_SALES_ORDER,
            EntityType::PurchaseOrder => DberpPermissionService::VIEW_PURCHASE_ORDER,
            EntityType::Receivable => DberpPermissionService::VIEW_CUSTOMER_RECEIVABLE,
            EntityType::Payable => DberpPermissionService::VIEW_SUPPLIER_PAYABLE,
        };
    }

    private function actionLabel(NavigationAction $action, EntityReference $reference): string
    {
        $key = 'dberp.entity_navigation.actions.'.$action->value;

        $translated = (string) __($key, ['label' => $reference->label]);

        return $translated === $key ? $reference->label : $translated;
    }

    private function target(
        NavigationAction $action,
        NavigationKind $kind,
        EntityReference $reference,
        string $label,
        string $url,
    ): NavigationTarget {
        return new NavigationTarget(
            action: $action,
            kind: $kind,
            entityType: $reference->entityType,
            entityId: $reference->entityId,
            label: $label,
            url: $url,
        );
    }
}
