<?php

declare(strict_types=1);

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

namespace App\Support\PageContext;

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\Context\ErpPageContext;
use App\Tools\Context\PageContextSource;
use App\Tools\Context\PageKind;
use App\Tools\Entity\EntityReference;
use App\Tools\Entity\EntityType;
use Illuminate\Database\Eloquent\Model;
use Throwable;

/**
 * 在进入 AI 运行时前重新校验页面上下文。伪造、过期、已删或无权限的上下文一律忽略。
 * 上下文不是授权：通过此处不代表可以绕过 ToolConsumerBoundary。
 */
final class ErpPageContextAuthorizer
{
    public function __construct(
        private readonly DberpPermissionService $permissions,
    ) {}

    public function authorize(User $user, mixed $candidate): ?ErpPageContext
    {
        $parsed = $candidate instanceof ErpPageContext
            ? $candidate
            : ErpPageContext::tryFromArray($candidate);

        if (! $parsed instanceof ErpPageContext) {
            return null;
        }

        return $this->authorizeIdentity(
            $user,
            $parsed->pageKind->value,
            $parsed->source->value,
            $parsed->entityRef->entityType->value,
            $parsed->entityRef->entityId,
        );
    }

    public function authorizeIdentity(
        User $user,
        mixed $pageKind,
        mixed $source,
        mixed $entityType,
        mixed $entityId,
    ): ?ErpPageContext {
        $kind = PageKind::tryFromMixed($pageKind);
        $src = PageContextSource::tryFromMixed($source);
        $type = EntityType::tryFromMixed($entityType);

        if (! $kind instanceof PageKind || ! $src instanceof PageContextSource || ! $type instanceof EntityType) {
            return null;
        }

        if ($kind->entityType() !== $type) {
            return null;
        }

        if (! is_numeric($entityId) || (int) $entityId <= 0) {
            return null;
        }

        $permission = $this->viewPermission($type);

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

        $record = $this->findRecord($type, (int) $entityId);

        if (! $record instanceof Model) {
            return null;
        }

        $reference = $this->referenceFromRecord($type, $record);

        if (! $reference instanceof EntityReference) {
            return null;
        }

        return new ErpPageContext($kind, $reference, $src);
    }

    private function findRecord(EntityType $type, int $id): ?Model
    {
        $modelClass = $this->modelClass($type);

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

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

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

    private function referenceFromRecord(EntityType $type, Model $record): ?EntityReference
    {
        return EntityReference::tryMake(
            $type,
            $record->getKey(),
            $this->stringAttribute($record, 'code'),
            $this->stringAttribute($record, 'name'),
        );
    }

    private function stringAttribute(Model $record, string $attribute): ?string
    {
        $value = $record->getAttribute($attribute);

        if (! is_string($value) && ! is_numeric($value)) {
            return null;
        }

        $normalized = trim((string) $value);

        return $normalized === '' ? null : $normalized;
    }

    /**
     * @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,
        };
    }

    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,
        };
    }
}
