<?php

namespace App\Http\Controllers\MerchantAdmin;

use App\Http\Controllers\Concerns\ResolvesMerchantContext;
use App\Http\Controllers\Controller;
use App\Models\Product;
use App\Models\ProductCategory;
use App\Models\ProductImportHistory;
use App\Support\CacheKeys;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Storage;
use Illuminate\Validation\Rule;
use Illuminate\View\View;
use Symfony\Component\HttpFoundation\StreamedResponse;

class ProductController extends Controller
{
    use ResolvesMerchantContext;

    protected array $statusOptions = ['draft', 'published', 'offline'];

    public function index(Request $request): View
    {
        $merchant = $this->merchant($request);

        $filters = $this->filters($request);
        $page = max((int) $request->integer('page', 1), 1);

        $summaryStats = Cache::remember(
            CacheKeys::merchantProductsSummary($merchant->id, $filters),
            now()->addMinutes(10),
            fn () => $this->buildSummaryStats($this->applyFilters(Product::query()->forMerchant($merchant->id), $filters))
        );

        $importHistoryData = $this->buildImportHistorySummaryData($merchant->id);

        return view('merchant_admin.products.index', [
            'merchant' => $merchant,
            'products' => Cache::remember(
                CacheKeys::merchantProductsList($merchant->id, $page, $filters),
                now()->addMinutes(10),
                fn () => $this->applySorting($this->applyFilters(Product::query()->forMerchant($merchant->id)->with('category'), $filters), $filters)
                    ->paginate(10)
                    ->withQueryString()
            ),
            'statusStats' => Cache::remember(
                CacheKeys::merchantProductsStatusStats($merchant->id, array_merge($filters, ['status' => ''])),
                now()->addMinutes(10),
                fn () => $this->buildStatusStats($this->applyFilters(Product::query()->forMerchant($merchant->id), array_merge($filters, ['status' => ''])))
            ),
            'summaryStats' => $summaryStats,
            'operationsFocus' => $this->buildOperationsFocus($summaryStats, $filters, $merchant->id),
            'workbenchLinks' => $this->workbenchLinks($merchant->id),
            'filters' => $filters,
            'filterOptions' => [
                'statuses' => $this->statusOptions,
                'sortOptions' => [
                    'latest' => '最新创建',
                    'price_asc' => '价格从低到高',
                    'price_desc' => '价格从高到低',
                    'stock_asc' => '库存从低到高',
                    'stock_desc' => '库存从高到低',
                ],
            ],
            'cacheMeta' => [
                'store' => config('cache.default'),
                'ttl' => '10m',
            ],
            'activeFilterSummary' => $this->buildActiveFilterSummary($filters),
            'statusLabels' => $this->statusLabels(),
            'categories' => ProductCategory::query()->where('merchant_id', $merchant->id)->orderBy('sort')->orderBy('id')->get(),
            'pageMeta' => [
                'current' => $page,
                'perPage' => 10,
            ],
            'importHistoryStats' => $importHistoryData['stats'],
            'importHistories' => $importHistoryData['histories'],
        ]);
    }

    public function store(Request $request): RedirectResponse
    {
        $merchantId = $this->merchantId($request);

        $data = $request->validate([
            'category_id' => ['nullable', 'integer'],
            'title' => ['required', 'string', 'max:200'],
            'slug' => ['required', 'string', 'max:200'],
            'sku' => ['nullable', 'string', 'max:120'],
            'summary' => ['nullable', 'string', 'max:500'],
            'price' => ['required', 'numeric', 'min:0'],
            'stock' => ['required', 'integer', 'min:0'],
        ]);

        $categoryIdRaw = $data['category_id'] ?? null;
        $categoryId = ($categoryIdRaw === null || $categoryIdRaw === '') ? null : (int) $categoryIdRaw;

        if ($categoryId !== null) {
            $category = ProductCategory::query()->where('merchant_id', $merchantId)->whereKey($categoryId)->first();
            if (! $category) {
                return redirect('/merchant-admin/products')->withErrors(['category_id' => '所选分类不存在或不属于当前商家。']);
            }
        }

        Product::query()->create([
            'merchant_id' => $merchantId,
            'category_id' => $categoryId,
            'title' => $data['title'],
            'slug' => $data['slug'],
            'sku' => $data['sku'] ?? '',
            'summary' => $data['summary'] ?? '',
            'content' => '',
            'price' => (float) $data['price'],
            'original_price' => (float) $data['price'],
            'stock' => (int) $data['stock'],
            'status' => 'draft',
            'images' => [],
        ]);

        $this->flushMerchantCaches($merchantId);

        return redirect('/merchant-admin/products')->with('success', '商品创建成功');
    }

    public function update(Request $request, int $id): RedirectResponse
    {
        $merchantId = $this->merchantId($request);

        $data = $request->validate([
            'title' => ['required', 'string', 'max:200'],
            'slug' => ['required', 'string', 'max:200'],
            'sku' => ['nullable', 'string', 'max:120'],
            'summary' => ['nullable', 'string', 'max:500'],
            'price' => ['required', 'numeric', 'min:0'],
            'stock' => ['required', 'integer', 'min:0'],
            'status' => ['required', 'string', Rule::in($this->statusOptions)],
            'category_id' => ['nullable', 'integer'],
        ]);

        $product = Product::query()->forMerchant($merchantId)->findOrFail($id);

        $categoryIdRaw = $data['category_id'] ?? null;
        $categoryId = ($categoryIdRaw === null || $categoryIdRaw === '') ? null : (int) $categoryIdRaw;

        if ($categoryId !== null) {
            $category = ProductCategory::query()->where('merchant_id', $merchantId)->whereKey($categoryId)->first();
            if (! $category) {
                return redirect('/merchant-admin/products')->withErrors(['category_id' => '所选分类不存在或不属于当前商家。']);
            }
        }

        $product->update([
            'title' => $data['title'],
            'slug' => $data['slug'],
            'sku' => $data['sku'] ?? '',
            'summary' => $data['summary'] ?? '',
            'price' => (float) $data['price'],
            'stock' => (int) $data['stock'],
            'status' => $data['status'],
            'category_id' => $categoryId,
        ]);

        $this->flushMerchantCaches($merchantId);

        return redirect('/merchant-admin/products')->with('success', '商品更新成功');
    }

    public function destroy(Request $request, int $id): RedirectResponse
    {
        $merchantId = $this->merchantId($request);

        Product::query()->forMerchant($merchantId)->whereKey($id)->delete();

        $this->flushMerchantCaches($merchantId);

        return redirect('/merchant-admin/products')->with('success', '商品已删除');
    }

    public function export(Request $request): StreamedResponse
    {
        $merchantId = $this->merchantId($request);

        $filters = $this->filters($request);
        $fileName = 'merchant_' . $merchantId . '_products_' . now()->format('Ymd_His') . '.csv';

        $exportSummary = $this->buildSummaryStats(
            $this->applyFilters(Product::query()->forMerchant($merchantId), $filters)
        );

        return response()->streamDownload(function () use ($merchantId, $filters, $exportSummary) {
            $handle = fopen('php://output', 'w');
            fwrite($handle, "\xEF\xBB\xBF");

            fputcsv($handle, ['导出信息', '商家商品导出']);
            fputcsv($handle, ['导出时间', now()->format('Y-m-d H:i:s')]);
            fputcsv($handle, ['状态', $this->statusLabel($filters['status'] ?? '')]);
            fputcsv($handle, ['分类', $this->categoryLabel((string) ($filters['category_id'] ?? ''), $merchantId)]);
            fputcsv($handle, ['关键词', $this->displayTextValue((string) ($filters['keyword'] ?? ''))]);
            fputcsv($handle, ['最低价格', $this->displayMoneyValue((string) ($filters['min_price'] ?? ''))]);
            fputcsv($handle, ['最高价格', $this->displayMoneyValue((string) ($filters['max_price'] ?? ''))]);
            fputcsv($handle, ['最低库存', $this->displayStockValue((string) ($filters['min_stock'] ?? ''))]);
            fputcsv($handle, ['最高库存', $this->displayStockValue((string) ($filters['max_stock'] ?? ''))]);
            fputcsv($handle, ['排序', $this->sortLabel((string) ($filters['sort'] ?? 'latest'))]);
            fputcsv($handle, ['导出商品数', $exportSummary['total_products'] ?? 0]);
            fputcsv($handle, ['导出总库存', $exportSummary['total_stock'] ?? 0]);
            fputcsv($handle, ['导出总货值', number_format((float) ($exportSummary['total_stock_value'] ?? 0), 2, '.', '')]);
            fputcsv($handle, ['导出平均售价', number_format((float) ($exportSummary['average_price'] ?? 0), 2, '.', '')]);
            fputcsv($handle, []);

            fputcsv($handle, [
                'ID',
                '分类ID',
                '分类名称',
                '分类标识',
                '商品标题',
                '商品Slug',
                'SKU',
                '售价',
                '划线价',
                '库存',
                '状态',
                '商品简介',
                '创建时间',
                '更新时间',
            ]);

            foreach ($this->applySorting($this->applyFilters(Product::query()->forMerchant($merchantId)->with('category'), $filters), $filters)->cursor() as $product) {
                fputcsv($handle, [
                    $product->id,
                    $product->category_id,
                    $product->category?->name ?? '',
                    $product->category?->slug ?? '',
                    $product->title,
                    $product->slug,
                    $product->sku,
                    number_format((float) $product->price, 2, '.', ''),
                    number_format((float) $product->original_price, 2, '.', ''),
                    $product->stock,
                    $this->statusLabel((string) $product->status),
                    $product->summary,
                    optional($product->created_at)?->format('Y-m-d H:i:s'),
                    optional($product->updated_at)?->format('Y-m-d H:i:s'),
                ]);
            }

            fclose($handle);
        }, $fileName, [
            'Content-Type' => 'text/csv; charset=UTF-8',
        ]);
    }

    public function downloadImportTemplate(Request $request): StreamedResponse
    {
        $merchantId = $this->merchantId($request);

        $fileName = 'merchant_' . $merchantId . '_product_import_template.csv';
        $header = $this->importTemplateHeader();

        return response()->streamDownload(function () use ($header) {
            $handle = fopen('php://output', 'w');
            fwrite($handle, "\xEF\xBB\xBF");
            fputcsv($handle, $header);
            fputcsv($handle, [
                '演示商品',
                'demo-product',
                'SKU-DEMO-001',
                '199.00',
                '299.00',
                100,
                'default',
                '演示商品简介',
                'published',
            ]);
            fclose($handle);
        }, $fileName, [
            'Content-Type' => 'text/csv; charset=UTF-8',
        ]);
    }

    public function import(Request $request): RedirectResponse
    {
        $merchantId = $this->merchantId($request);

        $file = $request->file('import_file') ?: $request->file('csv_file');

        if (! $file) {
            return redirect('/merchant-admin/products')->withErrors(['import_file' => '请上传 CSV 文件。']);
        }

        if (! $file instanceof \Illuminate\Http\UploadedFile || $file->getError() !== UPLOAD_ERR_OK) {
            return redirect('/merchant-admin/products')->withErrors(['import_file' => '上传失败，请重试。']);
        }

        $handle = fopen($file->getRealPath(), 'r');

        $headerRead = false;
        $expectedHeader = $this->importTemplateHeader();
        $importedCount = 0;
        $failedCount = 0;
        $failures = [];

        while (($row = fgetcsv($handle)) !== false) {
            if (! $headerRead) {
                $headerRead = true;

                if ($row !== $expectedHeader) {
                    fclose($handle);

                    return redirect('/merchant-admin/products')->withErrors(['import_file' => '上传的 CSV 头部与模板不匹配，请重新下载模板。']);
                }

                continue;
            }

            $data = $this->parseCsvRow($row, $expectedHeader);

            try {
                $this->importProductRow([
                    'title' => $data['title'] ?? '',
                    'slug' => $data['slug'] ?? '',
                    'sku' => $data['sku'] ?? '',
                    'price' => $data['price'] ?? 0,
                    'original_price' => $data['original_price'] ?? 0,
                    'stock' => $data['stock'] ?? 0,
                    'category_slug' => $data['category_slug'] ?? '',
                    'summary' => $data['summary'] ?? '',
                    'status' => $data['status'] ?? 'published',
                ], $merchantId, count($failures) + 2);

                $importedCount++;
            } catch (\InvalidArgumentException $exception) {
                $failedCount++;
                $failures[] = $this->buildImportFailureRow(count($failures) + 2, $data, $exception->getMessage());
            }
        }

        fclose($handle);

        if ($importedCount > 0) {
            $this->flushMerchantCaches($merchantId);
        }

        $failureFile = $failedCount > 0
            ? $this->storeImportFailuresCsv('merchant_' . $merchantId, $expectedHeader, $failures)
            : null;

        ProductImportHistory::query()->create([
            'scope' => 'merchant',
            'merchant_id' => $merchantId,
            'admin_id' => $this->merchantAdmin($request)->id,
            'file_name' => $file->getClientOriginalName() ?: $file->getFilename(),
            'success_count' => $importedCount,
            'failed_count' => $failedCount,
            'failure_file' => $failureFile,
            'imported_at' => now(),
        ]);

        $result = [
            'success' => $importedCount,
            'failed' => $failedCount,
            'failure_file' => $failureFile,
            'messages' => array_slice(array_map(fn (array $failure) => $failure['error'], $failures), 0, 10),
        ];

        return redirect('/merchant-admin/products')
            ->with('import_result', $result)
            ->with(
                $failedCount > 0 ? 'warning' : 'success',
                $failedCount > 0
                    ? "商品批量导入完成，成功 {$importedCount} 条，失败 {$failedCount} 条。"
                    : "商品批量导入完成，本次成功导入 {$importedCount} 条。"
            );
    }

    public function downloadImportFailures(Request $request, string $file): StreamedResponse
    {
        $merchantId = $this->merchantId($request);

        // 商家端限制：只能下载当前商家的 failure 文件（通过 history 表校验）
        $safeName = basename($file);
        $history = ProductImportHistory::query()
            ->where('scope', 'merchant')
            ->where('merchant_id', $merchantId)
            ->where('failure_file', $safeName)
            ->first();

        abort_unless($history, 404);

        $path = 'private/imports/product-failures/' . $safeName;
        abort_unless(Storage::disk('local')->exists($path), 404);

        return Storage::disk('local')->download($path, $safeName, [
            'Content-Type' => 'text/csv; charset=UTF-8',
        ]);
    }

    public function batchUpdate(Request $request): RedirectResponse
    {
        $merchantId = $this->merchantId($request);
        $returnUrl = $this->sanitizeBatchReturnUrl($request->input('return_url'), '/merchant-admin/products');

        $data = $request->validate([
            'product_ids' => ['required', 'array', 'min:1'],
            'product_ids.*' => ['integer', 'distinct'],
            'action' => ['required', 'string', Rule::in(['change_status', 'change_category'])],
            'status' => ['nullable', 'string', Rule::in($this->statusOptions)],
            'category_id' => ['nullable', 'integer'],
        ], [
            'product_ids.required' => '请先选择至少一个商品。',
            'product_ids.array' => '批量商品参数格式不正确。',
            'product_ids.min' => '请先选择至少一个商品。',
            'action.required' => '请选择批量操作类型。',
            'action.in' => '暂不支持该批量操作。',
            'status.in' => '批量状态仅支持 draft / published / offline。',
        ]);

        $productIds = collect($data['product_ids'] ?? [])->map(fn ($id) => (int) $id)->filter(fn ($id) => $id > 0)->values();

        if ($productIds->isEmpty()) {
            return redirect('/merchant-admin/products')->withErrors(['product_ids' => '请先选择至少一个商品。']);
        }

        $products = Product::query()->forMerchant($merchantId)->whereIn('id', $productIds)->get();

        if ($products->count() !== $productIds->count()) {
            return redirect('/merchant-admin/products')->withErrors(['product_ids' => '勾选商品中存在越权或已删除的数据，请刷新后重试。']);
        }

        $updatedCount = 0;

        if (($data['action'] ?? '') === 'change_status') {
            $status = trim((string) ($data['status'] ?? ''));

            if ($status === '') {
                return redirect($returnUrl)->withErrors(['status' => '请选择要批量更新的商品状态。']);
            }

            $updatedCount = Product::query()
                ->forMerchant($merchantId)
                ->whereIn('id', $productIds)
                ->where('status', '!=', $status)
                ->update([
                    'status' => $status,
                    'updated_at' => now(),
                ]);
        }

        if (($data['action'] ?? '') === 'change_category') {
            $categoryIdRaw = $data['category_id'] ?? null;
            $categoryId = ($categoryIdRaw === null || $categoryIdRaw === '') ? null : (int) $categoryIdRaw;

            if ($categoryId === null) {
                $updatedCount = Product::query()
                    ->forMerchant($merchantId)
                    ->whereIn('id', $productIds)
                    ->whereNotNull('category_id')
                    ->update([
                        'category_id' => null,
                        'updated_at' => now(),
                    ]);
            } else {
                $category = ProductCategory::query()->where('merchant_id', $merchantId)->where('id', $categoryId)->first();

                if (! $category) {
                    return redirect($returnUrl)->withErrors(['category_id' => '所选分类不存在或不属于当前商家。']);
                }

                $updatedCount = Product::query()
                    ->forMerchant($merchantId)
                    ->whereIn('id', $productIds)
                    ->update([
                        'category_id' => $categoryId,
                        'updated_at' => now(),
                    ]);
            }
        }

        if ($updatedCount > 0) {
            $this->flushMerchantCaches($merchantId);
        }

        return redirect($returnUrl)->with('success', "批量操作已完成，本次更新 {$updatedCount} 条商品。");
    }

    public function importHistories(Request $request): View
    {
        $merchantId = $this->merchantId($request);

        $filters = $this->importHistoryFilters($request);

        $query = ProductImportHistory::query()
            ->where('scope', 'merchant')
            ->where('merchant_id', $merchantId)
            ->with(['admin']);

        if (($filters['result_status'] ?? 'all') === 'success_only') {
            $query->where('failed_count', 0);
        } elseif (($filters['result_status'] ?? 'all') === 'has_failures') {
            $query->where('failed_count', '>', 0);
        }

        $start = $filters['start_date'] ?? null;
        $end = $filters['end_date'] ?? null;
        if ($start) {
            $query->whereDate('imported_at', '>=', $start);
        }
        if ($end) {
            $query->whereDate('imported_at', '<=', $end);
        }

        $sort = $filters['sort'] ?? 'latest';
        if ($sort === 'oldest') {
            $query->orderBy('imported_at')->orderBy('id');
        } else {
            $query->orderByDesc('imported_at')->orderByDesc('id');
        }

        $importHistories = $query->paginate(15)->withQueryString();
        $this->hydrateFailureAvailability($importHistories->getCollection());

        $statsQuery = (clone $query)->getQuery();
        $stats = ProductImportHistory::query()->fromSub($statsQuery, 'h')
            ->selectRaw('COUNT(*) as total_imports')
            ->selectRaw('COALESCE(SUM(success_count), 0) as total_success')
            ->selectRaw('COALESCE(SUM(failed_count), 0) as total_failed')
            ->selectRaw('SUM(CASE WHEN failed_count > 0 THEN 1 ELSE 0 END) as warning_imports')
            ->first();

        return view('merchant_admin.products.import_histories', [
            'importHistories' => $importHistories,
            'importHistoryFilters' => $filters,
            'importHistoryFilterOptions' => [
                'sorts' => [
                    'latest' => '最新导入',
                    'oldest' => '最早导入',
                ],
            ],
            'importHistoryStats' => [
                'total_imports' => (int) ($stats->total_imports ?? 0),
                'total_success' => (int) ($stats->total_success ?? 0),
                'total_failed' => (int) ($stats->total_failed ?? 0),
                'warning_imports' => (int) ($stats->warning_imports ?? 0),
            ],
        ]);
    }

    public function exportImportHistories(Request $request): StreamedResponse
    {
        $merchantId = $this->merchantId($request);

        $filters = $this->importHistoryFilters($request);

        $query = ProductImportHistory::query()
            ->where('scope', 'merchant')
            ->where('merchant_id', $merchantId)
            ->with(['admin']);

        if (($filters['result_status'] ?? 'all') === 'success_only') {
            $query->where('failed_count', 0);
        } elseif (($filters['result_status'] ?? 'all') === 'has_failures') {
            $query->where('failed_count', '>', 0);
        }

        $start = $filters['start_date'] ?? null;
        $end = $filters['end_date'] ?? null;
        if ($start) {
            $query->whereDate('imported_at', '>=', $start);
        }
        if ($end) {
            $query->whereDate('imported_at', '<=', $end);
        }

        $sort = $filters['sort'] ?? 'latest';
        if ($sort === 'oldest') {
            $query->orderBy('imported_at')->orderBy('id');
        } else {
            $query->orderByDesc('imported_at')->orderByDesc('id');
        }

        $fileName = 'merchant_' . $merchantId . '_product_import_histories_' . now()->format('Ymd_His') . '.csv';

        return response()->streamDownload(function () use ($query, $filters) {
            $handle = fopen('php://output', 'w');
            fwrite($handle, "\xEF\xBB\xBF");

            fputcsv($handle, ['导出信息', '商家商品导入历史']);
            fputcsv($handle, ['导出时间', now()->format('Y-m-d H:i:s')]);
            fputcsv($handle, ['导入结果', $filters['result_status'] ?? 'all']);
            fputcsv($handle, []);

            fputcsv($handle, ['ID', '导入时间', '上传文件', '成功', '失败', '操作者', 'failure_file']);

            foreach ($query->cursor() as $history) {
                fputcsv($handle, [
                    $history->id,
                    optional($history->imported_at)?->format('Y-m-d H:i:s'),
                    $history->file_name,
                    $history->success_count,
                    $history->failed_count,
                    $history->admin?->name ?? '-',
                    $history->failure_file ?? '',
                ]);
            }

            fclose($handle);
        }, $fileName, [
            'Content-Type' => 'text/csv; charset=UTF-8',
        ]);
    }

    protected function sanitizeBatchReturnUrl(?string $returnUrl, string $default): string
    {
        $candidate = trim((string) $returnUrl);

        if ($candidate === '') {
            return $default;
        }

        // 作为“批量操作返回地址”，只允许站内相对路径，并且需要稳定可控：
        // - 限定前缀（避免跳出 merchant-admin/products 语义域）
        // - 拒绝引号/尖括号（降低注入风险）
        // - 拒绝 nested back=（避免 URL 膨胀/绕过）
        if (! str_starts_with($candidate, '/merchant-admin/products')) {
            return $default;
        }

        if (preg_match('/["\'<>]/', $candidate)) {
            return $default;
        }

        if (preg_match('/(?:^|[?&])back=/', $candidate)) {
            return $default;
        }

        return $candidate;
    }

    protected function filters(Request $request): array
    {
        return [
            'status' => trim((string) $request->string('status')),
            'category_id' => trim((string) $request->string('category_id')),
            'keyword' => trim((string) $request->string('keyword')),
            'min_price' => trim((string) $request->string('min_price')),
            'max_price' => trim((string) $request->string('max_price')),
            'min_stock' => trim((string) $request->string('min_stock')),
            'max_stock' => trim((string) $request->string('max_stock')),
            'sort' => trim((string) $request->string('sort', 'latest')),
        ];
    }

    protected function applyFilters(Builder $query, array $filters): Builder
    {
        $categoryId = (int) ($filters['category_id'] ?? 0);

        return $query
            ->when(($filters['status'] ?? '') !== '', fn (Builder $builder) => $builder->where('status', $filters['status']))
            ->when($categoryId > 0, fn (Builder $builder) => $builder->where('category_id', $categoryId))
            ->when(($filters['keyword'] ?? '') !== '', function (Builder $builder) use ($filters) {
                $keyword = (string) ($filters['keyword'] ?? '');

                $builder->where(function (Builder $subQuery) use ($keyword) {
                    $subQuery->where('title', 'like', '%' . $keyword . '%')
                        ->orWhere('sku', 'like', '%' . $keyword . '%')
                        ->orWhere('slug', 'like', '%' . $keyword . '%');
                });
            })
            ->when(($filters['min_price'] ?? '') !== '' && is_numeric($filters['min_price']), fn (Builder $builder) => $builder->where('price', '>=', (float) $filters['min_price']))
            ->when(($filters['max_price'] ?? '') !== '' && is_numeric($filters['max_price']), fn (Builder $builder) => $builder->where('price', '<=', (float) $filters['max_price']))
            ->when(($filters['min_stock'] ?? '') !== '' && filter_var($filters['min_stock'], FILTER_VALIDATE_INT) !== false, fn (Builder $builder) => $builder->where('stock', '>=', (int) $filters['min_stock']))
            ->when(($filters['max_stock'] ?? '') !== '' && filter_var($filters['max_stock'], FILTER_VALIDATE_INT) !== false, fn (Builder $builder) => $builder->where('stock', '<=', (int) $filters['max_stock']));
    }

    protected function applySorting(Builder $query, array $filters): Builder
    {
        return match ($filters['sort'] ?? 'latest') {
            'price_asc' => $query->orderBy('price')->orderByDesc('id'),
            'price_desc' => $query->orderByDesc('price')->orderByDesc('id'),
            'stock_asc' => $query->orderBy('stock')->orderByDesc('id'),
            'stock_desc' => $query->orderByDesc('stock')->orderByDesc('id'),
            default => $query->latest(),
        };
    }

    protected function buildSummaryStats(Builder $query): array
    {
        $summary = (clone $query)
            ->selectRaw('COUNT(*) as total_products')
            ->selectRaw('COALESCE(SUM(stock), 0) as total_stock')
            ->selectRaw('COALESCE(SUM(price * stock), 0) as total_stock_value')
            ->selectRaw('COALESCE(AVG(price), 0) as average_price')
            ->first();

        return [
            'total_products' => (int) ($summary->total_products ?? 0),
            'total_stock' => (int) ($summary->total_stock ?? 0),
            'total_stock_value' => (float) ($summary->total_stock_value ?? 0),
            'average_price' => (float) ($summary->average_price ?? 0),
        ];
    }

    protected function buildStatusStats(Builder $query): array
    {
        $counts = (clone $query)
            ->selectRaw('status, COUNT(*) as aggregate')
            ->groupBy('status')
            ->pluck('aggregate', 'status');

        $stats = ['all' => (int) $counts->sum()];

        foreach ($this->statusOptions as $status) {
            $stats[$status] = (int) ($counts[$status] ?? 0);
        }

        return $stats;
    }

    protected function buildActiveFilterSummary(array $filters): array
    {
        return [
            '分类' => $this->categoryLabel((string) ($filters['category_id'] ?? ''), null),
            '状态' => $this->statusLabel((string) ($filters['status'] ?? '')),
            '关键词' => $this->displayTextValue((string) ($filters['keyword'] ?? '')),
            '价格区间' => $this->formatMoneyRange((string) ($filters['min_price'] ?? ''), (string) ($filters['max_price'] ?? '')),
            '库存区间' => $this->formatStockRange((string) ($filters['min_stock'] ?? ''), (string) ($filters['max_stock'] ?? '')),
            '排序' => $this->sortLabel((string) ($filters['sort'] ?? 'latest')),
        ];
    }

    protected function statusLabels(): array
    {
        return [
            'draft' => '草稿',
            'published' => '已上架',
            'offline' => '已下架',
        ];
    }

    protected function statusLabel(string $status): string
    {
        if ($status === '') {
            return '全部';
        }

        return $this->statusLabels()[$status] ?? $status;
    }

    protected function categoryLabel(string $categoryId, ?int $merchantId): string
    {
        $categoryId = trim($categoryId);
        if ($categoryId === '' || ! ctype_digit($categoryId) || (int) $categoryId <= 0) {
            return '全部';
        }

        $query = ProductCategory::query()->whereKey((int) $categoryId);
        if ($merchantId) {
            $query->where('merchant_id', $merchantId);
        }

        $category = $query->first();

        return $category?->name ?? ('分类 #' . $categoryId);
    }

    protected function formatMoneyRange(string $min, string $max): string
    {
        if ($min === '' && $max === '') {
            return '全部';
        }

        $minLabel = $min !== '' && is_numeric($min)
            ? ('¥' . number_format((float) $min, 2, '.', ''))
            : '不限';
        $maxLabel = $max !== '' && is_numeric($max)
            ? ('¥' . number_format((float) $max, 2, '.', ''))
            : '不限';

        return $minLabel . ' ~ ' . $maxLabel;
    }

    protected function formatStockRange(string $min, string $max): string
    {
        if ($min === '' && $max === '') {
            return '全部';
        }

        $minLabel = $min !== '' ? $min : '不限';
        $maxLabel = $max !== '' ? $max : '不限';

        return $minLabel . ' ~ ' . $maxLabel . ' 件';
    }

    protected function sortLabel(string $sort): string
    {
        return match ($sort) {
            'price_asc' => '价格从低到高',
            'price_desc' => '价格从高到低',
            'stock_asc' => '库存从低到高',
            'stock_desc' => '库存从高到低',
            default => '最新创建',
        };
    }

    protected function displayTextValue(string $value, string $default = '全部'): string
    {
        return trim($value) === '' ? $default : $value;
    }

    protected function displayMoneyValue(string $value): string
    {
        $value = trim($value);
        if ($value === '') {
            return '全部';
        }

        return is_numeric($value) ? ('¥' . number_format((float) $value, 2, '.', '')) : $value;
    }

    protected function displayStockValue(string $value): string
    {
        $value = trim($value);
        return $value === '' ? '全部' : ($value . ' 件');
    }

    protected function workbenchLinks(int $merchantId): array
    {
        return [
            'published_stock_desc' => '/merchant-admin/products?sort=stock_desc&status=published',
            'published_stock_asc' => '/merchant-admin/products?sort=stock_asc&status=published',
            'latest' => '/merchant-admin/products?sort=latest',
            'draft' => '/merchant-admin/products?status=draft&sort=latest',
            'current' => '/merchant-admin/products',
        ];
    }

    protected function buildOperationsFocus(array $summaryStats, array $filters, int $merchantId): array
    {
        $publishedCount = (int) Product::query()->forMerchant($merchantId)->where('status', 'published')->count();
        $lowStockCount = (int) Product::query()->forMerchant($merchantId)->where('status', 'published')->where('stock', '<=', 20)->count();
        $categoryCount = (int) ProductCategory::query()->where('merchant_id', $merchantId)->count();

        $links = $this->workbenchLinks($merchantId);
        $currentQuery = http_build_query(array_filter($filters, fn ($value) => $value !== null && $value !== '' && $value !== 'latest'));
        $currentUrl = $links['current'] . ($currentQuery !== '' ? ('?' . $currentQuery) : '');

        $workbench = [
            '高库存已上架' => $links['published_stock_desc'],
            '低库存补货' => $links['published_stock_asc'],
            '最近新增' => $links['latest'],
            '草稿待整理' => $links['draft'],
            '返回当前筛选视图' => $currentUrl,
        ];

        $signals = [
            '已上架商品' => $publishedCount,
            '低库存商品' => $lowStockCount,
            '分类覆盖数' => $categoryCount,
        ];

        $response = function (string $headline, array $actions) use ($workbench, $signals) {
            return [
                'headline' => $headline,
                'actions' => $actions,
                'workbench' => $workbench,
                'signals' => $signals,
            ];
        };

        $isPublished = ($filters['status'] ?? '') === 'published';
        $hasCategoryFilter = ($filters['category_id'] ?? '') !== '';
        $hasKeywordFilter = ($filters['keyword'] ?? '') !== '';
        $hasPriceRangeFilter = (($filters['min_price'] ?? '') !== '') || (($filters['max_price'] ?? '') !== '');

        $categoryLabel = $hasCategoryFilter ? $this->categoryLabel((string) ($filters['category_id'] ?? ''), $merchantId) : '';
        $keyword = (string) ($filters['keyword'] ?? '');
        $priceRange = $hasPriceRangeFilter ? $this->formatMoneyRange((string) ($filters['min_price'] ?? ''), (string) ($filters['max_price'] ?? '')) : '';

        $hasPublishedStockFocus = $isPublished
            && ((($filters['max_stock'] ?? '') !== '')
                || ((($filters['min_stock'] ?? '') !== '')
                    && is_numeric($filters['min_stock'])
                    && (int) $filters['min_stock'] <= 20));

        if (($filters['status'] ?? '') === 'draft') {
            return $response(
                '当前正在查看草稿商品，建议优先补齐标题、分类、价格与库存后再安排上架。',
                [
                    ['label' => '继续查看当前草稿', 'url' => $currentUrl],
                    ['label' => '去看已上架商品', 'url' => $links['published_stock_desc']],
                ]
            );
        }

        if ($hasPublishedStockFocus) {
            return $response(
                '当前筛选已聚焦已上架库存视角，建议优先确认低库存补货节奏，并同步观察高库存结构是否健康。',
                [
                    ['label' => '继续查看当前库存视角', 'url' => $currentUrl],
                    ['label' => '去看高库存商品', 'url' => $links['published_stock_desc']],
                ]
            );
        }

        if ($isPublished && $hasCategoryFilter && $hasKeywordFilter && $hasPriceRangeFilter) {
            return $response(
                '当前筛选已聚焦已上架的“' . $categoryLabel . '”分类下关键词“' . $keyword . '”命中的价格带 ' . $priceRange . ' 商品，建议优先核对在售商品命名、分类承接、价格梯度与搜索结果是否一致，并同步观察库存结构与转化表现是否健康。',
                [
                    ['label' => '继续查看当前已上架分类关键词价格带商品', 'url' => $currentUrl],
                    ['label' => '去看当前已上架分类关键词商品', 'url' => $this->buildUrl('/merchant-admin/products', array_merge($filters, ['min_price' => '', 'max_price' => '']))],
                ]
            );
        }

        if ($isPublished && $hasCategoryFilter && $hasKeywordFilter) {
            return $response(
                '当前筛选已聚焦已上架的“' . $categoryLabel . '”分类下关键词“' . $keyword . '”命中的商品，建议优先核对在售商品命名、分类承接与搜索结果是否一致，并同步观察价格带与库存结构是否健康。',
                [
                    ['label' => '继续查看当前已上架分类关键词商品', 'url' => $currentUrl],
                    ['label' => '去看当前已上架分类商品', 'url' => $this->buildUrl('/merchant-admin/products', array_merge($filters, ['keyword' => '']))],
                ]
            );
        }

        if ($isPublished && $hasCategoryFilter && $hasPriceRangeFilter) {
            return $response(
                '当前筛选已聚焦已上架的“' . $categoryLabel . '”分类下价格带 ' . $priceRange . ' 商品，建议优先核对该分类在售商品的价格结构、库存分布与转化表现是否协调。',
                [
                    ['label' => '继续查看当前已上架分类价格带商品', 'url' => $currentUrl],
                    ['label' => '继续查看当前已上架分类商品', 'url' => $this->buildUrl('/merchant-admin/products', array_merge($filters, ['min_price' => '', 'max_price' => '']))],
                ]
            );
        }

        if ($isPublished && $hasKeywordFilter && $hasPriceRangeFilter) {
            return $response(
                '当前筛选已聚焦已上架商品中关键词“' . $keyword . '”命中的价格带 ' . $priceRange . ' 结果，建议优先核对在售商品命名、卖点表达与价格梯度是否一致，并同步观察库存结构、转化表现与搜索承接是否健康。',
                [
                    ['label' => '继续查看当前已上架关键词价格带商品', 'url' => $currentUrl],
                    ['label' => '去看当前已上架关键词商品', 'url' => $this->buildUrl('/merchant-admin/products', array_merge($filters, ['min_price' => '', 'max_price' => '']))],
                ]
            );
        }

        if ($isPublished && $hasKeywordFilter) {
            return $response(
                '当前筛选已聚焦已上架商品中关键词“' . $keyword . '”命中的结果，建议优先核对在售商品命名、卖点表达与搜索承接是否一致，并同步观察价格带与库存结构是否健康。',
                [
                    ['label' => '继续查看当前已上架关键词商品', 'url' => $currentUrl],
                    ['label' => '去看全部已上架商品', 'url' => $links['published_stock_desc']],
                ]
            );
        }

        if ($hasCategoryFilter && $hasKeywordFilter && $hasPriceRangeFilter) {
            return $response(
                '当前筛选已聚焦“' . $categoryLabel . '”分类下关键词“' . $keyword . '”命中的价格带 ' . $priceRange . ' 商品，建议优先核对分类承接、命名卖点与价格梯度是否一致，并同步观察库存结构、转化表现与搜索承接是否健康。',
                [
                    ['label' => '继续查看当前分类关键词价格带商品', 'url' => $currentUrl],
                    ['label' => '去看当前分类关键词商品', 'url' => $this->buildUrl('/merchant-admin/products', array_merge($filters, ['min_price' => '', 'max_price' => '']))],
                ]
            );
        }

        if ($hasCategoryFilter && $hasKeywordFilter) {
            return $response(
                '当前筛选已聚焦“' . $categoryLabel . '”分类下关键词“' . $keyword . '”命中的商品，建议优先核对分类承接、命名卖点与搜索结果是否一致，并同步观察相关商品的价格带与库存结构。',
                [
                    ['label' => '继续查看当前分类关键词商品', 'url' => $currentUrl],
                    ['label' => '继续查看当前分类商品', 'url' => $this->buildUrl('/merchant-admin/products', array_merge($filters, ['keyword' => '']))],
                ]
            );
        }

        if ($isPublished && $hasCategoryFilter) {
            return $response(
                '当前筛选已聚焦已上架的“' . $categoryLabel . '”分类商品，建议优先核对该分类在售商品的价格带、库存结构与转化表现是否均衡。',
                [
                    ['label' => '继续查看当前已上架分类商品', 'url' => $currentUrl],
                    ['label' => '继续查看当前分类商品', 'url' => $this->buildUrl('/merchant-admin/products', array_merge($filters, ['status' => '', 'keyword' => '']))],
                ]
            );
        }

        if ($hasCategoryFilter && $hasPriceRangeFilter) {
            return $response(
                '当前筛选已聚焦“' . $categoryLabel . '”分类下价格带 ' . $priceRange . ' 商品，建议优先核对该分类价格结构是否连贯，并同步观察库存分布与转化表现是否健康。',
                [
                    ['label' => '继续查看当前分类价格带商品', 'url' => $currentUrl],
                    ['label' => '继续查看当前分类商品', 'url' => $this->buildUrl('/merchant-admin/products', array_merge($filters, ['min_price' => '', 'max_price' => '']))],
                ]
            );
        }

        if ($hasCategoryFilter) {
            return $response(
                '当前筛选已聚焦“' . $categoryLabel . '”分类商品，建议优先核对分类承接是否准确，并同步观察价格带与库存结构是否均衡。',
                [
                    ['label' => '继续查看当前分类商品', 'url' => $currentUrl],
                    ['label' => '去看低库存商品', 'url' => $links['published_stock_asc']],
                ]
            );
        }

        if ($hasKeywordFilter) {
            return $response(
                '当前筛选已聚焦关键词“' . $keyword . '”命中的商品，建议优先核对命名、卖点与搜索承接是否一致，并同步观察相关商品的价格带与库存结构。',
                [
                    ['label' => '继续查看当前关键词商品', 'url' => $currentUrl],
                    ['label' => '去看最近新增商品', 'url' => $links['latest']],
                ]
            );
        }

        if ($hasPriceRangeFilter) {
            return $response(
                '当前筛选已聚焦价格带 ' . $priceRange . ' 商品，建议优先核对定价梯度是否连贯，并同步观察库存结构与转化表现是否匹配。',
                [
                    ['label' => '继续查看当前价格带商品', 'url' => $currentUrl],
                    ['label' => '去看最近新增商品', 'url' => $links['latest']],
                ]
            );
        }

        if ($isPublished) {
            return $response(
                '当前正在查看已上架商品，建议优先关注库存结构、价格带与分类覆盖是否均衡。',
                [
                    ['label' => '去看低库存商品', 'url' => $links['published_stock_asc']],
                    ['label' => '继续查看已上架商品', 'url' => $currentUrl],
                ]
            );
        }

        if (($summaryStats['total_products'] ?? 0) <= 0) {
            return $response(
                '当前商家暂无商品，建议先补齐基础商品数据，再开始做上架与库存运营。',
                [
                    ['label' => '先看商品空白情况', 'url' => $links['latest']],
                    ['label' => '查看草稿商品', 'url' => $links['draft']],
                ]
            );
        }

        if (($summaryStats['total_products'] ?? 0) < 3) {
            return $response(
                '当前商家商品仍较少，建议优先查看最近新增与已上架商品，确认基础信息是否完整。',
                [
                    ['label' => '去看最近新增商品', 'url' => $links['latest']],
                    ['label' => '去看已上架商品', 'url' => $links['published_stock_desc']],
                ]
            );
        }

        return $response(
            $lowStockCount > 0
                ? '当前商家商品已形成基础规模，建议优先巡检低库存商品，并同步关注高库存结构是否均衡。'
                : '当前商家商品已形成基础规模，建议优先关注高库存结构与最近新增商品质量。',
            $lowStockCount > 0
                ? [
                    ['label' => '去看低库存商品', 'url' => $links['published_stock_asc']],
                    ['label' => '去看高库存商品', 'url' => $links['published_stock_desc']],
                ]
                : [
                    ['label' => '去看高库存商品', 'url' => $links['published_stock_desc']],
                    ['label' => '去看最近新增商品', 'url' => $links['latest']],
                ]
        );
    }

    protected function buildUrl(string $path, array $filters): string
    {
        $query = http_build_query(array_filter($filters, fn ($value) => $value !== null && $value !== '' && $value !== 'latest'));
        return $path . ($query !== '' ? ('?' . $query) : '');
    }

    protected function buildImportHistorySummaryData(int $merchantId): array
    {
        $histories = ProductImportHistory::query()
            ->where('scope', 'merchant')
            ->where('merchant_id', $merchantId)
            ->with(['admin'])
            ->orderByDesc('imported_at')
            ->orderByDesc('id')
            ->take(20)
            ->get();

        $this->hydrateFailureAvailability($histories);

        $stats = ProductImportHistory::query()
            ->where('scope', 'merchant')
            ->where('merchant_id', $merchantId)
            ->selectRaw('COUNT(*) as total_imports')
            ->selectRaw('COALESCE(SUM(success_count), 0) as total_success')
            ->selectRaw('COALESCE(SUM(failed_count), 0) as total_failed')
            ->selectRaw('SUM(CASE WHEN failed_count > 0 THEN 1 ELSE 0 END) as warning_imports')
            ->first();

        return [
            'histories' => $histories,
            'stats' => [
                'total_imports' => (int) ($stats->total_imports ?? 0),
                'total_success' => (int) ($stats->total_success ?? 0),
                'total_failed' => (int) ($stats->total_failed ?? 0),
                'warning_imports' => (int) ($stats->warning_imports ?? 0),
            ],
        ];
    }

    protected function hydrateFailureAvailability($histories): void
    {
        $disk = Storage::disk('local');
        $base = 'private/imports/product-failures/';

        foreach ($histories as $history) {
            $history->failure_file_available = $history->failure_file
                ? $disk->exists($base . basename((string) $history->failure_file))
                : false;
        }
    }

    protected function flushMerchantCaches(int $merchantId): void
    {
        Cache::add(CacheKeys::merchantProductsVersion($merchantId), 1, now()->addDays(30));
        Cache::increment(CacheKeys::merchantProductsVersion($merchantId));
        Cache::forget(CacheKeys::merchantDashboardStats($merchantId));
    }

    protected function importTemplateHeader(): array
    {
        return [
            'title',
            'slug',
            'sku',
            'price',
            'original_price',
            'stock',
            'category_slug',
            'summary',
            'status',
        ];
    }

    protected function parseCsvRow(array $row, array $header): array
    {
        $data = [];
        foreach ($header as $index => $column) {
            $data[$column] = $row[$index] ?? null;
        }
        return $data;
    }

    protected function buildImportFailureRow(int $line, array $data, string $error): array
    {
        return [
            'line' => $line,
            'data' => $data,
            'error' => $error,
        ];
    }

    protected function storeImportFailuresCsv(string $scope, array $header, array $failures): string
    {
        $fileName = $scope . '_product_import_failures_' . now()->format('Ymd_His') . '_' . substr(md5((string) microtime(true)), 0, 6) . '.csv';
        $path = 'private/imports/product-failures/' . $fileName;

        $lines = [];
        $lines[] = implode(',', array_merge($header, ['error']));
        foreach ($failures as $failure) {
            $data = $failure['data'] ?? [];
            $row = [];
            foreach ($header as $column) {
                $row[] = (string) ($data[$column] ?? '');
            }
            $row[] = (string) ($failure['error'] ?? '');
            $lines[] = $this->csvLine($row);
        }

        Storage::disk('local')->put($path, "\xEF\xBB\xBF" . implode("\n", $lines));

        return $fileName;
    }

    protected function csvLine(array $fields): string
    {
        $handle = fopen('php://temp', 'r+');
        fputcsv($handle, $fields);
        rewind($handle);
        $line = stream_get_contents($handle);
        fclose($handle);

        return rtrim((string) $line, "\n\r");
    }

    protected function importProductRow(array $data, int $merchantId, int $line): void
    {
        $title = trim((string) ($data['title'] ?? ''));
        $slug = trim((string) ($data['slug'] ?? ''));

        if ($title === '') {
            throw new \InvalidArgumentException("第 {$line} 行 title 不能为空");
        }

        if ($slug === '') {
            throw new \InvalidArgumentException("第 {$line} 行 slug 不能为空");
        }

        $categoryId = null;
        $categorySlug = trim((string) ($data['category_slug'] ?? ''));
        if ($categorySlug !== '') {
            $category = ProductCategory::query()->where('merchant_id', $merchantId)->where('slug', $categorySlug)->first();
            if (! $category) {
                throw new \InvalidArgumentException("第 {$line} 行 category_slug 不存在");
            }
            $categoryId = $category->id;
        }

        $status = trim((string) ($data['status'] ?? 'published'));
        if (! in_array($status, $this->statusOptions, true)) {
            $status = 'published';
        }

        $price = is_numeric($data['price'] ?? null) ? (float) $data['price'] : 0.0;
        $originalPrice = is_numeric($data['original_price'] ?? null) ? (float) $data['original_price'] : $price;
        $stock = filter_var($data['stock'] ?? 0, FILTER_VALIDATE_INT) !== false ? (int) $data['stock'] : 0;

        Product::query()->forMerchant($merchantId)->updateOrCreate([
            'merchant_id' => $merchantId,
            'slug' => $slug,
        ], [
            'category_id' => $categoryId,
            'title' => $title,
            'sku' => trim((string) ($data['sku'] ?? '')),
            'summary' => trim((string) ($data['summary'] ?? '')),
            'content' => '',
            'price' => $price,
            'original_price' => $originalPrice,
            'stock' => $stock,
            'status' => $status,
            'images' => [],
        ]);
    }

    protected function importHistoryFilters(Request $request): array
    {
        $resultStatus = trim((string) $request->string('import_result_status', 'all'));
        $timeRange = trim((string) $request->string('import_time_range', 'all'));
        $rawStart = trim((string) $request->string('start_date'));
        $rawEnd = trim((string) $request->string('end_date'));
        $sort = trim((string) $request->string('import_sort', 'latest'));

        $dateErrors = [];

        $startDate = null;
        $endDate = null;

        if ($timeRange === 'today') {
            $startDate = now()->toDateString();
            $endDate = now()->toDateString();
        } elseif ($timeRange === 'last_7_days') {
            $startDate = now()->subDays(6)->toDateString();
            $endDate = now()->toDateString();
        } elseif ($timeRange === 'custom') {
            if ($rawStart !== '' && ! $this->isValidDate($rawStart)) {
                $dateErrors[] = '开始日期格式不正确，请使用 YYYY-MM-DD。';
            }
            if ($rawEnd !== '' && ! $this->isValidDate($rawEnd)) {
                $dateErrors[] = '结束日期格式不正确，请使用 YYYY-MM-DD。';
            }
            if ($rawStart !== '' && $rawEnd !== '' && $this->isValidDate($rawStart) && $this->isValidDate($rawEnd) && $rawStart > $rawEnd) {
                $dateErrors[] = '开始日期不能晚于结束日期。';
            }

            $startDate = $rawStart !== '' ? $rawStart : null;
            $endDate = $rawEnd !== '' ? $rawEnd : null;
        }

        return [
            'result_status' => in_array($resultStatus, ['all', 'success_only', 'has_failures'], true) ? $resultStatus : 'all',
            'time_range' => in_array($timeRange, ['all', 'today', 'last_7_days', 'custom'], true) ? $timeRange : 'all',
            'start_date' => $startDate,
            'end_date' => $endDate,
            'sort' => in_array($sort, ['latest', 'oldest'], true) ? $sort : 'latest',
            'date_errors' => $dateErrors,
        ];
    }

    protected function isValidDate(string $value): bool
    {
        try {
            $date = Carbon::createFromFormat('Y-m-d', $value);
        } catch (\Throwable $exception) {
            return false;
        }

        return $date && $date->format('Y-m-d') === $value;
    }
}
