<?php

declare(strict_types=1);

namespace App\Business\Integration;

use App\Business\Integration\Contracts\ChannelSyncAdapterInterface;
use App\Business\Integration\DTOs\SyncContext;
use App\Business\Integration\Enums\IntegrationMappingStatus;
use App\Business\Integration\Enums\IntegrationObjectType;
use App\Business\Integration\Enums\IntegrationSyncStatus;
use App\Business\Integration\Exceptions\IntegrationGovernanceException;
use App\Business\Integration\Support\IntegrationLogger;
use App\Business\MasterData\Enums\SettlementMethod;
use App\Business\Sales\Enums\SalesOrderStatus;
use App\Business\Sales\Enums\SalesSourceType;
use App\Business\Settlement\Enums\ReceivableStatus;
use App\Models\DberpCustomer;
use App\Models\DberpIntegrationMapping;
use App\Models\DberpIntegrationSyncLog;
use App\Models\DberpInventoryBalance;
use App\Models\DberpInventoryTransaction;
use App\Models\DberpProduct;
use App\Models\DberpProductCategory;
use App\Models\DberpSalesOrder;
use App\Models\DberpSalesOrderItem;
use App\Models\DberpUnit;
use App\Services\DberpAuditService;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Collection as EloquentCollection;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Str;

final class DbshopIntegrationService
{
    public function __construct(
        private readonly ChannelSyncAdapterInterface $adapter,
        private readonly IntegrationMappingService $mappingService,
        private readonly DberpAuditService $auditService,
    ) {}

    public function syncProducts(array $filters = []): int
    {
        $count = 0;

        foreach ($this->adapter->pullProducts($filters) as $payload) {
            try {
                $this->syncSingleProduct($payload);
                $count++;
            } catch (\Throwable) {
                continue;
            }
        }

        return $count;
    }

    public function syncCustomers(array $filters = []): int
    {
        $count = 0;

        foreach ($this->adapter->pullCustomers($filters) as $payload) {
            try {
                $this->syncSingleCustomer($payload);
                $count++;
            } catch (\Throwable) {
                continue;
            }
        }

        return $count;
    }

    public function syncOrders(array $filters = []): int
    {
        $count = 0;

        foreach ($this->adapter->pullOrders($filters) as $payload) {
            try {
                $this->syncSingleOrder($payload);
                $count++;
            } catch (\Throwable) {
                continue;
            }
        }

        return $count;
    }

    public function syncInventoryBatch(): int
    {
        $count = 0;

        /** @var EloquentCollection<int, DberpProduct> $products */
        $products = DberpProduct::query()
            ->whereHas('integrationMappings', function ($query): void {
                $query->where('source_system', 'dbshop')->where('object_type', IntegrationObjectType::Product->value);
            })
            ->get();

        foreach ($products as $product) {
            if ($this->syncInventoryForProduct($product) !== null) {
                $count++;
            }
        }

        return $count;
    }

    public function syncInventoryForTransaction(DberpInventoryTransaction $transaction): ?DberpIntegrationSyncLog
    {
        $product = $transaction->product;

        if (! $product instanceof DberpProduct) {
            return null;
        }

        return $this->syncInventoryForProduct($product, $transaction);
    }

    public function syncInventoryForProduct(DberpProduct $product, ?DberpInventoryTransaction $transaction = null): ?DberpIntegrationSyncLog
    {
        $mapping = $product->integrationMappings()
            ->where('source_system', 'dbshop')
            ->where('object_type', IntegrationObjectType::Product->value)
            ->first();

        if (! $mapping instanceof DberpIntegrationMapping) {
            return null;
        }

        $payload = $this->inventoryPayload($product, $mapping, $transaction);

        $context = new SyncContext(
            channel: 'dbshop',
            direction: 'export',
            objectType: IntegrationObjectType::Inventory->value,
            reference: $transaction?->business_number ?? $product->code,
            correlationId: (string) Str::uuid(),
            sourceSystem: 'dberp',
            targetSystem: 'dbshop',
            idempotencyKey: $this->inventoryIdempotencyKey($payload),
            operatorId: Auth::id(),
        );

        $log = IntegrationLogger::start($context, $product, '库存联动开始', $payload);

        try {
            $result = $this->adapter->pushInventory($payload, $context);

            $product->forceFill([
                'integration_sync_status' => IntegrationMappingStatus::Synced->value,
                'last_integration_synced_at' => now(),
            ])->save();

            IntegrationLogger::success($log, '库存联动完成', ['response' => $result]);
            $this->auditService->log('dberp.integration.inventory_synced', $product, null, [
                'external_product_id' => $mapping->external_id,
                'available_quantity' => $payload['available_quantity'],
                'realtime_quantity' => $payload['realtime_quantity'],
            ]);

            return $log;
        } catch (\Throwable $exception) {
            $product->forceFill([
                'integration_sync_status' => IntegrationMappingStatus::Failed->value,
            ])->save();

            IntegrationLogger::failure($log, '库存联动失败', ['exception' => $exception->getMessage()], $this->resolveErrorType($exception));
            $this->auditService->log('dberp.integration.inventory_sync_failed', $product, null, [
                'external_product_id' => $mapping->external_id,
                'error' => $exception->getMessage(),
            ]);

            return $log;
        }
    }

    public function retrySyncLog(DberpIntegrationSyncLog $log): void
    {
        $log->increment('retry_count');
        $log->refresh();

        $this->assertRetryable($log);

        if ($this->hasSuccessfulIdempotentSync($log)) {
            $retryLog = $this->startRetryAttemptLog($log, '检测到幂等命中，重试已拦截');

            IntegrationLogger::skipped($retryLog, '同一幂等键已成功处理，跳过重复重试。', [
                'retry_of_log_id' => $log->getKey(),
            ], 'idempotent_blocked');
            IntegrationLogger::updateRetryResult($log, IntegrationSyncStatus::Skipped, '同一幂等键已成功处理，已跳过本次重试。');

            $this->auditService->log('dberp.integration.retry_requested', $log->relatedModel, null, [
                'channel' => $log->channel,
                'object_type' => $log->sync_object_type,
                'reference' => $log->reference,
                'retry_count' => $log->retry_count,
                'result' => 'idempotent_blocked',
            ]);

            return;
        }

        try {
            match ($log->sync_object_type) {
                IntegrationObjectType::Product->value => $this->retryProductImport($log),
                IntegrationObjectType::Customer->value => $this->retryCustomerImport($log),
                IntegrationObjectType::Order->value => $this->retryOrderImport($log),
                IntegrationObjectType::Inventory->value => $this->retryInventoryExport($log),
                default => throw new IntegrationGovernanceException('retry_validation', '当前联通对象不支持重试。'),
            };

            IntegrationLogger::updateRetryResult($log, IntegrationSyncStatus::Success, '重试已发起，结果已记录到新的联通日志。');
        } catch (IntegrationGovernanceException $exception) {
            $retryLog = $this->startRetryAttemptLog($log, '重试校验失败');

            IntegrationLogger::failure($retryLog, $exception->getMessage(), [
                'retry_of_log_id' => $log->getKey(),
            ], $exception->errorType());
            IntegrationLogger::updateRetryResult($log, IntegrationSyncStatus::Failed, $exception->getMessage());

            throw $exception;
        } catch (\Throwable $exception) {
            IntegrationLogger::updateRetryResult($log, IntegrationSyncStatus::Failed, $exception->getMessage());

            throw $exception;
        }

        $this->auditService->log('dberp.integration.retry_requested', $log->relatedModel, null, [
            'channel' => $log->channel,
            'object_type' => $log->sync_object_type,
            'reference' => $log->reference,
            'retry_count' => $log->retry_count,
            'result' => $log->latest_retry_status,
        ]);
    }

    /**
     * @param  array<string, mixed>  $payload
     */
    private function syncSingleProduct(array $payload): DberpProduct
    {
        $context = new SyncContext(
            channel: 'dbshop',
            direction: 'import',
            objectType: IntegrationObjectType::Product->value,
            reference: (string) ($payload['external_code'] ?? $payload['external_id']),
            correlationId: (string) Str::uuid(),
            sourceSystem: 'dbshop',
            idempotencyKey: $this->makeIdempotencyKey(IntegrationObjectType::Product, [(string) ($payload['external_id'] ?? ''), (string) ($payload['external_sku'] ?? '')]),
            operatorId: Auth::id(),
        );

        $log = IntegrationLogger::start($context, null, '商品同步开始', [
            'external_id' => $payload['external_id'] ?? null,
            'external_code' => $payload['external_code'] ?? null,
            'source_document_no' => $payload['external_code'] ?? null,
            'external_reference' => $payload['external_id'] ?? null,
        ]);

        try {
            $category = $this->resolveCategory($payload['category'] ?? []);
            $unit = $this->resolveDefaultUnit();
            [$product, $matchStrategy] = $this->resolveProductForSync($payload);

            $product->fill([
                'code' => $product->exists ? $product->code : $this->makeCode('DBSP', (string) ($payload['external_code'] ?? $payload['external_id'])),
                'name' => (string) $payload['name'],
                'barcode' => $payload['barcode'] ?? null,
                'mnemonic_code' => $payload['mnemonic_code'] ?? null,
                'category_id' => $category?->getKey(),
                'brand_name' => $payload['brand_name'] ?? null,
                'spec_model' => $payload['spec_model'] ?? null,
                'unit_id' => $unit->getKey(),
                'cost_price' => (string) ($payload['cost_price'] ?? '0.0000'),
                'sales_price' => (string) ($payload['sales_price'] ?? '0.0000'),
                'source_platform' => 'dbshop',
                'external_product_id' => (string) $payload['external_id'],
                'external_sku' => $payload['external_sku'] ?? null,
                'external_spec' => $payload['spec_model'] ?? null,
                'integration_sync_status' => IntegrationMappingStatus::Synced->value,
                'last_integration_synced_at' => now(),
                'is_enabled' => (bool) ($payload['is_enabled'] ?? true),
                'sort_order' => $product->sort_order ?? 0,
                'remark' => $product->remark ?: 'DBShop 联通基础同步建立。',
                'can_purchase' => (bool) ($payload['can_purchase'] ?? true),
                'can_sale' => (bool) ($payload['can_sale'] ?? true),
                'manages_inventory' => (bool) ($payload['manages_inventory'] ?? true),
            ]);
            $product->save();

            $this->mappingService->upsertMapping(
                'dbshop',
                IntegrationObjectType::Product,
                (string) $payload['external_id'],
                $product,
                attributes: [
                    'external_code' => $payload['external_code'] ?? null,
                    'external_name' => $payload['name'] ?? null,
                    'external_sku' => $payload['external_sku'] ?? null,
                    'external_spec' => $payload['spec_model'] ?? null,
                    'mapping_payload' => $payload,
                ],
                status: IntegrationMappingStatus::Synced,
                matchStrategy: $matchStrategy,
            );

            IntegrationLogger::success($log, '商品同步完成', [
                'local_product_id' => $product->getKey(),
                'match_strategy' => $matchStrategy,
            ]);

            $this->auditService->log('dberp.integration.product_synced', $product, null, [
                'external_product_id' => $payload['external_id'],
                'match_strategy' => $matchStrategy,
            ]);

            return $product;
        } catch (IntegrationGovernanceException $exception) {
            $this->captureProductMappingFailure($payload, $exception->getMessage(), $exception->errorType());
            IntegrationLogger::failure($log, '商品同步失败', ['exception' => $exception->getMessage()], $exception->errorType());

            throw $exception;
        } catch (\Throwable $exception) {
            $this->captureProductMappingFailure($payload, $exception->getMessage(), $this->resolveErrorType($exception));
            IntegrationLogger::failure($log, '商品同步失败', ['exception' => $exception->getMessage()], $this->resolveErrorType($exception));
            throw $exception;
        }
    }

    /**
     * @param  array<string, mixed>  $payload
     */
    private function syncSingleCustomer(array $payload): DberpCustomer
    {
        $context = new SyncContext(
            channel: 'dbshop',
            direction: 'import',
            objectType: IntegrationObjectType::Customer->value,
            reference: (string) ($payload['external_id'] ?? $payload['mobile'] ?? $payload['name']),
            correlationId: (string) Str::uuid(),
            sourceSystem: 'dbshop',
            idempotencyKey: $this->makeIdempotencyKey(IntegrationObjectType::Customer, [(string) ($payload['external_id'] ?? ''), (string) ($payload['mobile'] ?? '')]),
            operatorId: Auth::id(),
        );

        $log = IntegrationLogger::start($context, null, '客户映射同步开始', [
            'external_id' => $payload['external_id'] ?? null,
            'external_reference' => $payload['external_id'] ?? null,
        ]);

        try {
            [$customer, $matchStrategy] = $this->resolveCustomerForSync($payload);

            $customer->fill([
                'code' => $customer->exists ? $customer->code : $this->makeCode('DBSC', (string) ($payload['external_id'] ?? $payload['mobile'] ?? $payload['name'])),
                'name' => (string) $payload['name'],
                'contact_person' => (string) $payload['name'],
                'mobile' => $payload['mobile'] ?? null,
                'phone' => $payload['mobile'] ?? null,
                'email' => $payload['email'] ?? null,
                'address' => $payload['address'] ?? null,
                'settlement_method' => $customer->settlement_method ?: SettlementMethod::Cash->value,
                'salesperson_employee_id' => $customer->salesperson_employee_id,
                'source_platform' => 'dbshop',
                'external_member_id' => (string) $payload['external_id'],
                'integration_sync_status' => IntegrationMappingStatus::Synced->value,
                'last_integration_synced_at' => now(),
                'is_enabled' => true,
                'sort_order' => $customer->sort_order ?? 0,
                'remark' => $customer->remark ?: 'DBShop 会员映射基础同步建立。',
            ]);
            $customer->save();

            $this->mappingService->upsertMapping(
                'dbshop',
                IntegrationObjectType::Customer,
                (string) $payload['external_id'],
                $customer,
                attributes: [
                    'external_name' => $payload['name'] ?? null,
                    'mapping_payload' => $payload,
                ],
                status: IntegrationMappingStatus::Synced,
                matchStrategy: $matchStrategy,
            );

            IntegrationLogger::success($log, '客户映射同步完成', [
                'local_customer_id' => $customer->getKey(),
                'match_strategy' => $matchStrategy,
            ]);

            $this->auditService->log('dberp.integration.customer_synced', $customer, null, [
                'external_member_id' => $payload['external_id'],
                'match_strategy' => $matchStrategy,
            ]);

            return $customer;
        } catch (IntegrationGovernanceException $exception) {
            $this->captureCustomerMappingFailure($payload, $exception->getMessage(), $exception->errorType());
            IntegrationLogger::failure($log, '客户映射同步失败', ['exception' => $exception->getMessage()], $exception->errorType());

            throw $exception;
        } catch (\Throwable $exception) {
            $this->captureCustomerMappingFailure($payload, $exception->getMessage(), $this->resolveErrorType($exception));
            IntegrationLogger::failure($log, '客户映射同步失败', ['exception' => $exception->getMessage()], $this->resolveErrorType($exception));
            throw $exception;
        }
    }

    /**
     * @param  array<string, mixed>  $payload
     */
    private function syncSingleOrder(array $payload): DberpSalesOrder
    {
        $context = new SyncContext(
            channel: 'dbshop',
            direction: 'import',
            objectType: IntegrationObjectType::Order->value,
            reference: (string) ($payload['order_no'] ?? $payload['external_id']),
            correlationId: (string) Str::uuid(),
            sourceSystem: 'dbshop',
            idempotencyKey: $this->makeIdempotencyKey(IntegrationObjectType::Order, [(string) ($payload['order_no'] ?? ''), (string) ($payload['external_id'] ?? '')]),
            operatorId: Auth::id(),
        );

        $log = IntegrationLogger::start($context, null, '订单导入开始', [
            'external_id' => $payload['external_id'] ?? null,
            'order_no' => $payload['order_no'] ?? null,
            'source_document_no' => $payload['order_no'] ?? null,
            'external_reference' => $payload['external_id'] ?? null,
        ]);

        try {
            $existing = DberpSalesOrder::query()
                ->where('source_platform', 'dbshop')
                ->where('external_order_no', (string) $payload['order_no'])
                ->first();

            if ($existing instanceof DberpSalesOrder) {
                $existing->forceFill([
                    'last_sync_attempt_at' => now(),
                ])->save();

                IntegrationLogger::success($log, '订单已存在，跳过重复导入', [
                    'local_order_id' => $existing->getKey(),
                ]);

                return $existing;
            }

            $customer = $this->mappingService->resolveCustomer(
                'dbshop',
                (string) $payload['customer_external_id'],
                $payload['customer_mobile'] ?? null,
                $payload['customer_name'] ?? null,
            );

            if (! $customer instanceof DberpCustomer) {
                $this->syncCustomers(['external_id' => (string) $payload['customer_external_id']]);
                $customer = $this->mappingService->resolveCustomer(
                    'dbshop',
                    (string) $payload['customer_external_id'],
                    $payload['customer_mobile'] ?? null,
                    $payload['customer_name'] ?? null,
                );
            }

            if (! $customer instanceof DberpCustomer) {
                throw new IntegrationGovernanceException('mapping_missing', '未找到可映射的客户。');
            }

            return DB::transaction(function () use ($payload, $customer, $log): DberpSalesOrder {
                $status = $this->mapExternalOrderStatus((string) ($payload['status'] ?? 'pending_payment'));
                $items = $payload['items'] ?? [];

                $order = DberpSalesOrder::query()->create([
                    'code' => $this->makeCode('DBSO', (string) ($payload['order_no'] ?? $payload['external_id'])),
                    'customer_id' => $customer->getKey(),
                    'handler_employee_id' => null,
                    'source_type' => SalesSourceType::DbShop->value,
                    'source_platform' => 'dbshop',
                    'external_order_id' => (string) $payload['external_id'],
                    'external_order_no' => (string) $payload['order_no'],
                    'status' => $status->value,
                    'business_time' => Carbon::parse((string) $payload['business_time']),
                    'discount_rate' => '0.0000',
                    'total_quantity' => '0.0000',
                    'total_amount' => '0.0000',
                    'discount_amount' => '0.0000',
                    'final_amount' => '0.0000',
                    'receivable_amount' => '0.0000',
                    'received_amount' => '0.0000',
                    'unreceived_amount' => '0.0000',
                    'receivable_status' => ReceivableStatus::Uncollected->value,
                    'confirmed_at' => in_array($status, [SalesOrderStatus::Confirmed, SalesOrderStatus::Closed], true) ? now() : null,
                    'closed_at' => $status === SalesOrderStatus::Closed ? now() : null,
                    'voided_at' => $status === SalesOrderStatus::Voided ? now() : null,
                    'imported_at' => now(),
                    'last_sync_attempt_at' => now(),
                    'remark' => $payload['remark'] ?? 'DBShop 订单导入。',
                ]);

                foreach ($items as $index => $itemPayload) {
                    $product = $this->mappingService->resolveProduct(
                        'dbshop',
                        (string) $itemPayload['external_product_id'],
                        $itemPayload['external_sku'] ?? null,
                    );

                    if (! $product instanceof DberpProduct) {
                        $this->syncProducts(['external_id' => (string) $itemPayload['external_product_id']]);
                        $product = $this->mappingService->resolveProduct(
                            'dbshop',
                            (string) $itemPayload['external_product_id'],
                            $itemPayload['external_sku'] ?? null,
                        );
                    }

                    if (! $product instanceof DberpProduct) {
                        throw new IntegrationGovernanceException('mapping_missing', '未找到可映射的商品。');
                    }

                    $quantity = (string) ($itemPayload['quantity'] ?? '0.0000');
                    $unitPrice = (string) ($itemPayload['unit_price'] ?? '0.0000');
                    $discountAmount = (string) ($itemPayload['discount_amount'] ?? '0.0000');
                    $lineAmount = bcmul($quantity, $unitPrice, 4);
                    $finalAmount = bcsub($lineAmount, $discountAmount, 4);

                    DberpSalesOrderItem::query()->create([
                        'order_id' => $order->getKey(),
                        'product_id' => $product->getKey(),
                        'quantity' => $quantity,
                        'unit_price' => $unitPrice,
                        'line_amount' => $lineAmount,
                        'discount_amount' => $discountAmount,
                        'final_amount' => $finalAmount,
                        'shipped_quantity' => '0.0000',
                        'returned_quantity' => '0.0000',
                        'external_line_id' => $itemPayload['external_line_id'] ?? null,
                        'external_product_id' => $itemPayload['external_product_id'] ?? null,
                        'external_sku' => $itemPayload['external_sku'] ?? null,
                        'sort_order' => $index + 1,
                        'remark' => 'DBShop 订单导入明细',
                    ]);
                }

                $order->refresh();
                $order->forceFill([
                    'receivable_amount' => $status === SalesOrderStatus::Voided ? '0.0000' : (string) $order->final_amount,
                    'received_amount' => '0.0000',
                    'unreceived_amount' => $status === SalesOrderStatus::Voided ? '0.0000' : (string) $order->final_amount,
                    'receivable_status' => ReceivableStatus::Uncollected->value,
                ])->save();

                $this->mappingService->upsertMapping(
                    'dbshop',
                    IntegrationObjectType::Order,
                    (string) $payload['external_id'],
                    $order,
                    attributes: [
                        'external_code' => $payload['order_no'] ?? null,
                        'external_name' => $payload['customer_name'] ?? null,
                        'source_document_no' => $payload['order_no'] ?? null,
                        'mapping_payload' => $payload,
                    ],
                    status: IntegrationMappingStatus::Synced,
                    matchStrategy: 'external_order_no',
                );

                IntegrationLogger::success($log, '订单导入完成', [
                    'local_order_id' => $order->getKey(),
                    'customer_id' => $customer->getKey(),
                ]);

                $this->auditService->log('dberp.integration.order_imported', $order, null, [
                    'external_order_id' => $payload['external_id'],
                    'external_order_no' => $payload['order_no'],
                ]);

                return $order;
            });
        } catch (IntegrationGovernanceException $exception) {
            $this->captureOrderMappingFailure($payload, $exception->getMessage(), $exception->errorType());
            IntegrationLogger::failure($log, '订单导入失败', ['exception' => $exception->getMessage()], $exception->errorType());

            throw $exception;
        } catch (\Throwable $exception) {
            $this->captureOrderMappingFailure($payload, $exception->getMessage(), $this->resolveErrorType($exception));
            IntegrationLogger::failure($log, '订单导入失败', ['exception' => $exception->getMessage()], $this->resolveErrorType($exception));
            throw $exception;
        }
    }

    /**
     * @param  array<string, mixed>  $categoryPayload
     */
    private function resolveCategory(array $categoryPayload): ?DberpProductCategory
    {
        if ($categoryPayload === []) {
            return null;
        }

        $mapping = $this->mappingService->findMapping('dbshop', IntegrationObjectType::ProductCategory, (string) ($categoryPayload['external_id'] ?? ''));

        if ($mapping?->localModel instanceof DberpProductCategory) {
            return $mapping->localModel;
        }

        $category = DberpProductCategory::query()
            ->where('name', (string) ($categoryPayload['name'] ?? ''))
            ->first();

        if (! $category instanceof DberpProductCategory) {
            $category = DberpProductCategory::query()->create([
                'code' => $this->makeCode('DBCT', (string) ($categoryPayload['external_code'] ?? $categoryPayload['external_id'] ?? $categoryPayload['name'] ?? 'CAT')),
                'name' => (string) ($categoryPayload['name'] ?? 'DBShop 分类'),
                'parent_id' => null,
                'sort_order' => 0,
                'is_enabled' => true,
                'remark' => 'DBShop 商品分类映射基础建立。',
            ]);
        }

        if (($categoryPayload['external_id'] ?? null) !== null) {
            $this->mappingService->upsertMapping(
                'dbshop',
                IntegrationObjectType::ProductCategory,
                (string) $categoryPayload['external_id'],
                $category,
                attributes: [
                    'external_code' => $categoryPayload['external_code'] ?? null,
                    'external_name' => $categoryPayload['name'] ?? null,
                    'mapping_payload' => $categoryPayload,
                ],
                status: IntegrationMappingStatus::Synced,
                matchStrategy: 'name',
            );
        }

        return $category;
    }

    private function resolveDefaultUnit(): DberpUnit
    {
        return DberpUnit::query()->firstOrCreate(
            ['name' => '件'],
            [
                'code' => 'DBUNIT-JIAN',
                'sort_order' => 0,
                'is_enabled' => true,
                'remark' => 'DBShop 联通基础默认单位。',
            ],
        );
    }

    /**
     * @param  array<string, mixed>  $payload
     * @return array{0:DberpProduct,1:string}
     */
    private function resolveProductForSync(array $payload): array
    {
        $mapping = $this->mappingService->findMapping('dbshop', IntegrationObjectType::Product, (string) $payload['external_id']);

        if ($mapping?->localModel instanceof DberpProduct) {
            $product = $mapping->localModel;

            $matchStrategy = match (true) {
                $product->external_product_id === ($payload['external_id'] ?? null) => 'external_id',
                $product->external_sku === ($payload['external_sku'] ?? null) => 'external_sku',
                $product->barcode === ($payload['barcode'] ?? null) => 'barcode',
                $product->name === ($payload['name'] ?? null) => 'name',
                default => 'mapping',
            };

            return [$product, $matchStrategy];
        }

        $candidates = $this->mappingService->matchProductCandidates(
            (string) $payload['external_id'],
            $payload['external_sku'] ?? null,
            $payload['barcode'] ?? null,
            $payload['external_code'] ?? null,
            $payload['name'] ?? null,
        );

        if ($candidates->count() > 1) {
            $mapping = $this->mappingService->upsertPlaceholderMapping(
                'dbshop',
                IntegrationObjectType::Product,
                (string) $payload['external_id'],
                attributes: [
                    'external_code' => $payload['external_code'] ?? null,
                    'external_name' => $payload['name'] ?? null,
                    'external_sku' => $payload['external_sku'] ?? null,
                    'external_spec' => $payload['spec_model'] ?? null,
                    'mapping_payload' => $payload,
                ],
                status: IntegrationMappingStatus::Conflict,
                matchStrategy: 'candidate_conflict',
            );

            $this->mappingService->markConflict($mapping, '存在多个可匹配的 ERP 商品，请先在映射治理中人工处理。');

            throw new IntegrationGovernanceException('mapping_conflict', '存在多个可匹配的 ERP 商品，请先在映射治理中人工处理。');
        }

        $product = $candidates->first();

        if ($product instanceof DberpProduct) {
            $matchStrategy = match (true) {
                $product->external_product_id === ($payload['external_id'] ?? null) => 'external_id',
                $product->external_sku === ($payload['external_sku'] ?? null) => 'external_sku',
                $product->barcode === ($payload['barcode'] ?? null) => 'barcode',
                $product->name === ($payload['name'] ?? null) => 'name',
                default => 'mapping',
            };

            return [$product, $matchStrategy];
        }

        return [new DberpProduct(), 'created'];
    }

    /**
     * @param  array<string, mixed>  $payload
     * @return array{0:DberpCustomer,1:string}
     */
    private function resolveCustomerForSync(array $payload): array
    {
        $mapping = $this->mappingService->findMapping('dbshop', IntegrationObjectType::Customer, (string) $payload['external_id']);

        if ($mapping?->localModel instanceof DberpCustomer) {
            $customer = $mapping->localModel;

            $matchStrategy = match (true) {
                $customer->external_member_id === ($payload['external_id'] ?? null) => 'external_id',
                $customer->mobile === ($payload['mobile'] ?? null) => 'mobile',
                $customer->name === ($payload['name'] ?? null) => 'name',
                default => 'mapping',
            };

            return [$customer, $matchStrategy];
        }

        $candidates = $this->mappingService->matchCustomerCandidates(
            (string) $payload['external_id'],
            $payload['mobile'] ?? null,
            $payload['name'] ?? null,
        );

        if ($candidates->count() > 1) {
            $mapping = $this->mappingService->upsertPlaceholderMapping(
                'dbshop',
                IntegrationObjectType::Customer,
                (string) $payload['external_id'],
                attributes: [
                    'external_name' => $payload['name'] ?? null,
                    'mapping_payload' => $payload,
                ],
                status: IntegrationMappingStatus::Conflict,
                matchStrategy: 'candidate_conflict',
            );

            $this->mappingService->markConflict($mapping, '存在多个可匹配的 ERP 客户，请先在映射治理中人工处理。');

            throw new IntegrationGovernanceException('mapping_conflict', '存在多个可匹配的 ERP 客户，请先在映射治理中人工处理。');
        }

        $customer = $candidates->first();

        if ($customer instanceof DberpCustomer) {
            $matchStrategy = match (true) {
                $customer->external_member_id === ($payload['external_id'] ?? null) => 'external_id',
                $customer->mobile === ($payload['mobile'] ?? null) => 'mobile',
                $customer->name === ($payload['name'] ?? null) => 'name',
                default => 'mapping',
            };

            return [$customer, $matchStrategy];
        }

        return [new DberpCustomer(), 'created'];
    }

    private function mapExternalOrderStatus(string $status): SalesOrderStatus
    {
        return match ($status) {
            'paid', 'processing' => SalesOrderStatus::Confirmed,
            'completed' => SalesOrderStatus::Closed,
            'cancelled', 'closed' => SalesOrderStatus::Voided,
            default => SalesOrderStatus::Draft,
        };
    }

    private function makeCode(string $prefix, string $value): string
    {
        $sanitized = Str::upper((string) preg_replace('/[^A-Za-z0-9]+/', '', $value));
        $suffix = $sanitized !== '' ? $sanitized : strtoupper(Str::random(8));

        return Str::limit($prefix.'-'.$suffix, 64, '');
    }

    /**
     * @return array<string, mixed>
     */
    private function inventoryPayload(DberpProduct $product, DberpIntegrationMapping $mapping, ?DberpInventoryTransaction $transaction = null): array
    {
        $balance = DberpInventoryBalance::query()
            ->where('product_id', $product->getKey())
            ->selectRaw('COALESCE(SUM(realtime_quantity), 0) as realtime_quantity, COALESCE(SUM(available_quantity), 0) as available_quantity')
            ->first();

        return [
            'external_id' => $mapping->external_id,
            'external_sku' => $mapping->external_sku ?: $product->external_sku,
            'product_code' => $product->code,
            'product_name' => $product->name,
            'realtime_quantity' => (string) ($balance?->realtime_quantity ?? '0.0000'),
            'available_quantity' => (string) ($balance?->available_quantity ?? '0.0000'),
            'source_transaction_id' => $transaction?->getKey(),
            'source_transaction_number' => $transaction?->business_number,
            'source_transaction_type' => $transaction?->business_type,
        ];
    }

    private function assertRetryable(DberpIntegrationSyncLog $log): void
    {
        if (! in_array($log->status, [IntegrationSyncStatus::Failed->value, IntegrationSyncStatus::Skipped->value], true)) {
            throw new IntegrationGovernanceException('retry_validation', '只有失败或已跳过的联通日志允许重试。');
        }

        if ($log->sync_object_type === null || $log->sync_object_type === '') {
            throw new IntegrationGovernanceException('retry_validation', '当前联通日志缺少对象类型，无法重试。');
        }
    }

    private function hasSuccessfulIdempotentSync(DberpIntegrationSyncLog $log): bool
    {
        $idempotencyKey = $log->context['idempotency_key'] ?? null;

        if (! is_string($idempotencyKey) || $idempotencyKey === '') {
            return false;
        }

        return DberpIntegrationSyncLog::query()
            ->where('source_system', $log->source_system)
            ->where('sync_object_type', $log->sync_object_type)
            ->where('status', IntegrationSyncStatus::Success->value)
            ->where('context->idempotency_key', $idempotencyKey)
            ->whereKeyNot($log->getKey())
            ->exists();
    }

    private function startRetryAttemptLog(DberpIntegrationSyncLog $log, string $message): DberpIntegrationSyncLog
    {
        $context = new SyncContext(
            channel: $log->channel,
            direction: $log->direction,
            objectType: (string) $log->sync_object_type,
            reference: (string) $log->reference,
            correlationId: (string) Str::uuid(),
            sourceSystem: $log->source_system,
            targetSystem: $log->target_system,
            idempotencyKey: $log->context['idempotency_key'] ?? null,
            operatorId: Auth::id(),
        );

        return IntegrationLogger::start($context, $log->relatedModel, $message, array_merge($log->context ?? [], [
            'retry_of_log_id' => $log->getKey(),
            'retry_count' => $log->retry_count,
            'source_document_no' => $log->source_document_no,
            'external_reference' => $log->external_reference,
        ]));
    }

    private function retryProductImport(DberpIntegrationSyncLog $log): void
    {
        $externalId = (string) ($log->context['external_id'] ?? $log->external_reference ?? $log->reference ?? '');

        if ($externalId === '') {
            throw new IntegrationGovernanceException('retry_validation', '当前商品联通日志缺少外部商品标识。');
        }

        $payload = $this->firstPayload($this->adapter->pullProducts(['external_id' => $externalId]));

        if ($payload === null) {
            throw new IntegrationGovernanceException('retry_validation', '未找到可重试的 DBShop 商品数据。');
        }

        $this->syncSingleProduct($payload);
    }

    private function retryCustomerImport(DberpIntegrationSyncLog $log): void
    {
        $externalId = (string) ($log->context['external_id'] ?? $log->external_reference ?? $log->reference ?? '');

        if ($externalId === '') {
            throw new IntegrationGovernanceException('retry_validation', '当前客户联通日志缺少外部会员标识。');
        }

        $payload = $this->firstPayload($this->adapter->pullCustomers(['external_id' => $externalId]));

        if ($payload === null) {
            throw new IntegrationGovernanceException('retry_validation', '未找到可重试的 DBShop 会员数据。');
        }

        $this->syncSingleCustomer($payload);
    }

    private function retryOrderImport(DberpIntegrationSyncLog $log): void
    {
        $orderNo = (string) ($log->context['order_no'] ?? $log->source_document_no ?? $log->reference ?? '');

        if ($orderNo === '') {
            throw new IntegrationGovernanceException('retry_validation', '当前订单联通日志缺少外部订单号。');
        }

        $payload = $this->firstPayload($this->adapter->pullOrders(['order_no' => $orderNo]));

        if ($payload === null) {
            throw new IntegrationGovernanceException('retry_validation', '未找到可重试的 DBShop 订单数据。');
        }

        $this->syncSingleOrder($payload);
    }

    private function retryInventoryExport(DberpIntegrationSyncLog $log): void
    {
        if (! $log->relatedModel instanceof DberpProduct) {
            throw new IntegrationGovernanceException('retry_validation', '当前库存联通日志缺少关联商品，无法重试。');
        }

        $this->syncInventoryForProduct($log->relatedModel);
    }

    private function firstPayload(array $payloads): ?array
    {
        return $payloads[0] ?? null;
    }

    private function captureProductMappingFailure(array $payload, string $message, string $errorType): void
    {
        $externalId = (string) ($payload['external_id'] ?? '');

        if ($externalId === '') {
            return;
        }

        $mapping = $this->mappingService->upsertPlaceholderMapping(
            'dbshop',
            IntegrationObjectType::Product,
            $externalId,
            attributes: [
                'external_code' => $payload['external_code'] ?? null,
                'external_name' => $payload['name'] ?? null,
                'external_sku' => $payload['external_sku'] ?? null,
                'external_spec' => $payload['spec_model'] ?? null,
                'mapping_payload' => $payload,
            ],
            status: $errorType === 'mapping_conflict' ? IntegrationMappingStatus::Conflict : IntegrationMappingStatus::Failed,
            matchStrategy: $errorType === 'mapping_conflict' ? 'candidate_conflict' : null,
        );

        if ($errorType === 'mapping_conflict') {
            $this->mappingService->markConflict($mapping, $message, $errorType);

            return;
        }

        $this->mappingService->markFailed($mapping, $message, $errorType);
    }

    private function captureCustomerMappingFailure(array $payload, string $message, string $errorType): void
    {
        $externalId = (string) ($payload['external_id'] ?? '');

        if ($externalId === '') {
            return;
        }

        $mapping = $this->mappingService->upsertPlaceholderMapping(
            'dbshop',
            IntegrationObjectType::Customer,
            $externalId,
            attributes: [
                'external_name' => $payload['name'] ?? null,
                'mapping_payload' => $payload,
            ],
            status: $errorType === 'mapping_conflict' ? IntegrationMappingStatus::Conflict : IntegrationMappingStatus::Failed,
            matchStrategy: $errorType === 'mapping_conflict' ? 'candidate_conflict' : null,
        );

        if ($errorType === 'mapping_conflict') {
            $this->mappingService->markConflict($mapping, $message, $errorType);

            return;
        }

        $this->mappingService->markFailed($mapping, $message, $errorType);
    }

    private function captureOrderMappingFailure(array $payload, string $message, string $errorType): void
    {
        $externalId = (string) ($payload['external_id'] ?? '');

        if ($externalId === '') {
            return;
        }

        $mapping = $this->mappingService->upsertPlaceholderMapping(
            'dbshop',
            IntegrationObjectType::Order,
            $externalId,
            attributes: [
                'external_code' => $payload['order_no'] ?? null,
                'external_name' => $payload['customer_name'] ?? null,
                'source_document_no' => $payload['order_no'] ?? null,
                'mapping_payload' => $payload,
            ],
            status: IntegrationMappingStatus::Failed,
            matchStrategy: 'external_order_no',
        );

        $this->mappingService->markFailed($mapping, $message, $errorType);
    }

    private function makeIdempotencyKey(IntegrationObjectType $objectType, array $parts): string
    {
        $normalizedParts = array_filter(array_map(static fn ($value): string => trim((string) $value), $parts), static fn (string $value): bool => $value !== '');

        return implode(':', array_merge(['dbshop', $objectType->value], $normalizedParts));
    }

    private function inventoryIdempotencyKey(array $payload): string
    {
        return $this->makeIdempotencyKey(IntegrationObjectType::Inventory, [
            $payload['external_id'] ?? '',
            $payload['source_transaction_number'] ?? $payload['product_code'] ?? '',
            $payload['realtime_quantity'] ?? '',
            $payload['available_quantity'] ?? '',
        ]);
    }

    private function resolveErrorType(\Throwable $exception): string
    {
        if ($exception instanceof IntegrationGovernanceException) {
            return $exception->errorType();
        }

        return 'sync_failed';
    }
}