<?php
declare (strict_types = 1);

namespace app\admin\controller\v1;

use app\admin\model\v1\ProductCompatModel;
use app\admin\model\v1\ProductCompatModelsModel;
use app\admin\model\v1\ProductCompatPartsModel;
use app\admin\model\v1\ProductCompatTypesModel;
use app\admin\validate\v1\ProductCompatValidate;
use Generator;
use think\db\Query;

class ProductCompat
{
    /**
     * 产品兼容性数据列表
     */
    public function index()
    {
        $params = request()->param([
            'compatible_model',
            'model',
            'type_id/d',
            'part_id/d',
            'brand_name',
            'page/d' => 1,
            'size/d' => 10
        ]);

        $list = ProductCompatModel::withoutField([
                'language_id',
                'deleted_at'
            ])
            ->with([
                'types' => function ($query) {
                    $query->field(['id', 'name']);
                },
                'parts' => function ($query) {
                    $query->field(['id', 'name']);
                },
                'compatible_models' => function ($query) {
                    $query->field(['compat_id', 'compatible_model']);
                }
            ])
            ->language(request()->lang_id)
            ->where(function ($query) use($params) {
                if (!empty($params['type_id'])) {
                    $query->where('type_id', '=', $params['type_id']);
                }
                if (!empty($params['part_id'])) {
                    $query->where('part_id', '=', $params['part_id']);
                }
            })
            ->withSearch(['model', 'brand_name', 'compatible_model'], [
                'model'            => $params['model'] ?? null,
                'brand_name'       => $params['brand_name'] ?? null,
                'compatible_model' => $params['compatible_model'] ?? null,
            ])
            ->order(['updated_at' => 'desc'])
            ->paginate([
                'list_rows' => $params['size'],
                'page'      => $params['page'],
            ])
            ?->bindAttr('types', ['type_name' => 'name'])
            ?->bindAttr('parts', ['part_name' => 'name'])
            ?->hidden(['types', 'parts'])
            ?->each(function ($item) {
                $compatible_models = [];
                foreach ($item['compatible_models'] as $model) {
                    $compatible_models[] = $model['compatible_model'];
                }
                $item['compatible_models'] = implode(',', $compatible_models);
            });

        return success("获取成功", $list);
    }

    /**
     * 导入产品兼容性数据
     */
    public function import()
    {
        // 获取上传文件
        $file = request()->file('file');
        if (empty($file)) {
            return error('请上传文件');
        }
        $lang_id = request()->lang_id;

        // 读取文件
        $keys_map = [
            'A' => 'type_name',
            'B' => 'part_name',
            'C' => 'brand_name',
            'D' => 'level_name',
            'E' => 'spec_name',
            'F' => 'series_name',
            'G' => 'model',
            'H' => 'frequency',
            'I' => 'capacity',
            'J' => 'compatible_models',
        ];

        $chunk       = 500; // 每批次处理的条数
        $items       = 0;   // 已处理的条数
        $xlsx_data   = [];
        $xlsx_reader = xlsx_stream_reader($file->getRealPath(), 2, $keys_map, true);

        \think\facade\Db::startTrans();
        try {
            foreach ($xlsx_reader as $row) {
                $items++;
                $row['seq_no'] = $items; // 记录行序号，防止后续顺序打乱
                $xlsx_data[]   = $row;
                if ($items % $chunk == 0) {
                    // 每500条，为一批次进行处理
                    $this->handleImport($xlsx_data, $lang_id);
                    $xlsx_data = [];
                }
            }

            if (!empty($xlsx_data)) {
                // 处理剩余的
                $this->handleImport($xlsx_data, $lang_id);
            }
        } catch (\Throwable $th) {
            \think\facade\Db::rollback();
            return error($th->getMessage());
        }

        \think\facade\Db::commit();
        return success('操作成功');
    }
    private function handleImport(array $xlsx_data, int $lang_id): void
    {
        list(
            $models,
            $exists_compat_ids,
            $compat_models,
            $compat_datas
        ) = $this->matchExistsCompatData($xlsx_data, $lang_id);

        // 校验数据并组装sql语句
        $raw_sql = $this->validateAndBuildSql($compat_datas);
        if (false === \think\facade\Db::execute($raw_sql)) {
            throw new \Exception(sprintf('第【%s】行执行导入 SQL 失败', implode(',', array_column($xlsx_data, 'seq_no'))));
        }

        // 清除旧兼容型号数据
        if (!empty($exists_compat_ids)) {
            ProductCompatModelsModel::destroy(function ($query) use($exists_compat_ids) {
                $query->where('compat_id', 'in', $exists_compat_ids);
            });
        }
        // 为不遗漏id重新查询
        $compat_map = ProductCompatModel::language($lang_id)
            ->modelName($models)
            ->column('id', 'model');
        // 插入兼容型号数据
        $compatible_models = [];
        foreach ($compat_models as $m) {
            $compat_id = $compat_map[$m['model']];
            foreach ($m['compatible_models'] as $compatible_model) {
                $compatible_models[] = [
                    'compat_id'        => $compat_id,
                    'compatible_model' => $compatible_model,
                ];
            }
        }
        if (!empty($compatible_models)) {
            ProductCompatModelsModel::insertAll($compatible_models);
        }
    }
    private function matchExistsCompatData(array $datas, int $lang_id)
    {
        $models     = array_column($datas, 'model');
        $compat_map = ProductCompatModel::language($lang_id)
            ->modelName($models)
            ->column('id', 'model');

        $type_names = array_unique(array_column($datas, 'type_name'));
        $type_map   = ProductCompatTypesModel::language($lang_id)
            ->typeName($type_names)
            ->column('id', 'name');

        $part_names = array_unique(array_column($datas, 'part_name'));
        $part_map   = ProductCompatPartsModel::language($lang_id)
            ->partName($part_names)
            ->column('id', 'name');

        // 匹配已存在数据的主键、类型id及配件id
        $exists_compat_ids = [];
        $errors            = [];
        $compat_models     = [];
        foreach ($datas as &$d) {
            // 确认要更新的数据的主键
            $d['id'] = null;
            if (isset($compat_map[$d['model']])) {
                $d['id']             = $compat_map[$d['model']];
                $exists_compat_ids[] = $d['id'];
            }
            // 所属语言
            $d['language_id'] = $lang_id;

            // 确认产品类型id
            if (empty($type_map[$d['type_name']])) {
                $errors[] = "第【{$d['seq_no']}】行，产品类型【{$d['type_name']}】无效";
                continue;
            }
            $d['type_id'] = $type_map[$d['type_name']];

            // 确认配件类型id
            if (empty($part_map[$d['part_name']])) {
                $errors[] = "第【{$d['seq_no']}】行，配件类型【{$d['part_name']}】无效";
                continue;
            }
            $d['part_id'] = $part_map[$d['part_name']];

            // 处理兼容型号数据
            $compatible_models_str = str_replace('，', ',', $d['compatible_models']);
            $compatible_models_arr = explode(',', $compatible_models_str);
            if (!empty($compatible_models_arr)) {
                $compat_models[] = [
                    'model'             => $d['model'],
                    'compatible_models' => $compatible_models_arr,
                ];
            }
        }
        unset($d);
        if (!empty($errors)) {
            throw new \Exception(implode("\n", $errors));
        }

        return [$models, $exists_compat_ids, $compat_models, $datas];
    }
    private function validateAndBuildSql(array $compat_datas)
    {
        $sql_values = [];
        $validate   = new ProductCompatValidate;
        foreach ($compat_datas as $compat) {
            // 校验数据
            $scene  = is_null($compat['id']) ? 'add' : 'edit';
            if (!$validate->scene($scene)->check($compat)) {
                throw new \Exception(sprintf('第【%s】行%s', $compat['seq_no'], $validate->getError()));
            }

            // 组装 sql values
            $sql_values[] = $this->buildSqlValue($compat);
        }

        return $this->buildRawSql($sql_values);
    }
    private function buildSqlValue(array $compat)
    {
        $pdo = \think\facade\Db::connect()->getPdo();
        return sprintf(
            '(%d, %d, %d, %d, %s, %s, %s, %s, %s, %s, %s)',
            $compat['id'],
            $compat['language_id'],
            $compat['type_id'],
            $compat['part_id'],
            is_string($compat['model']) ? $pdo->quote($compat['model']) : $compat['model'],
            is_string($compat['brand_name']) ? $pdo->quote($compat['brand_name']) : $compat['brand_name'],
            is_string($compat['level_name']) ? $pdo->quote($compat['level_name']) : $compat['level_name'],
            is_string($compat['spec_name']) ? $pdo->quote($compat['spec_name']) : $compat['spec_name'],
            is_string($compat['series_name']) ? $pdo->quote($compat['series_name']) : $compat['series_name'],
            is_string($compat['capacity']) ? $pdo->quote($compat['capacity']) : $compat['capacity'],
            is_string($compat['frequency']) ? $pdo->quote($compat['frequency']) : $compat['frequency']
        );
    }
    private function buildRawSql(array $values)
    {
        return sprintf(
            'INSERT INTO %s (
                `id`,
                `language_id`,
                `type_id`,
                `part_id`,
                `model`,
                `brand_name`,
                `level_name`,
                `spec_name`,
                `series_name`,
                `capacity`,
                `frequency`
            ) VALUES %s
            ON DUPLICATE KEY UPDATE
                `type_id`        = VALUES(`type_id`),
                `part_id`        = VALUES(`part_id`),
                `model`          = VALUES(`model`),
                `brand_name`     = VALUES(`brand_name`),
                `level_name`     = VALUES(`level_name`),
                `spec_name`      = VALUES(`spec_name`),
                `series_name`    = VALUES(`series_name`),
                `capacity`       = VALUES(`capacity`),
                `frequency`      = VALUES(`frequency`)
            ',
            (new ProductCompatModel)->getTable(),
            implode(',', $values)
        );
    }

    /**
     * 产品兼容性数据导出
     */
    public function export()
    {
        $params = request()->param([
            'compatible_model',
            'model',
            'type_id',
            'part_id',
            'brand_name'
        ]);

        $query = ProductCompatModel::withoutField([
                'language_id',
                'deleted_at'
            ])
            ->language(1)
            ->where(function ($query) use($params) {
                if (!empty($params['type_id'])) {
                    $query->typeId($params['type_id']);
                }
                if (!empty($params['part_id'])) {
                    $query->partId($params['part_id']);
                }
            })
            ->withSearch(['model', 'brand_name', 'compatible_model'], [
                'model'            => $params['model'] ?? null,
                'brand_name'       => $params['brand_name'] ?? null,
                'compatible_model' => $params['compatible_model'] ?? null,
            ]);

        // 类型/配件为字典小表，一次性载入内存映射，避免每批预加载关联查询
        $type_map = ProductCompatTypesModel::column('name', 'id');
        $part_map = ProductCompatPartsModel::column('name', 'id');

        $schema = [
            'id'                => 'id',
            'type_name'         => '产品类型',
            'brand_name'        => '品牌',
            'model'             => '型号',
            'part_name'         => '配件',
            'level_name'        => '级别',
            'spec_name'         => '规格',
            'series_name'       => '系列',
            'capacity'          => '容量',
            'frequency'         => '频率',
            'compatible_models' => '兼容型号',
            'praise_count'      => '点赞数',
            'sort'              => '排序',
            'created_at'        => '新增时间',
            'updated_at'        => '更新时间'
        ];

        return xlsx_download_xaccel($this->xlsxRowGenerator($query, $type_map, $part_map), $schema, '产品兼容性' . date('YmdHis'));
    }
    private function xlsxRowGenerator(Query $query, array $type_map, array $part_map, int $page_size = 500): Generator
    {
        // 主键游标分页，避免 offset 深翻页的性能问题
        $last_id = 0;
        while (true) {
            $items = (clone $query)
                ->where('id', '>', $last_id)
                ->order(['id' => 'asc'])
                ->limit($page_size)
                ->select();
            if ($items->isEmpty()) {
                break;
            }

            // 仅查询当前批次的兼容型号，避免一次性拉取全量 id 及巨型 IN 子句
            $compat_map = [];
            $compat_ids = $items->column('id');
            if (!empty($compat_ids)) {
                $compat_models = ProductCompatModelsModel::field(['compat_id', 'compatible_model'])
                    ->where('compat_id', 'in', $compat_ids)
                    ->order(['id' => 'asc'])
                    ->select()
                    ->toArray();
                foreach ($compat_models as $compat_model) {
                    $compat_map[$compat_model['compat_id']][] = $compat_model['compatible_model'];
                }
            }

            foreach ($items as $item) {
                yield [
                    'id'                => $item['id'],
                    'type_name'         => $type_map[$item['type_id']] ?? '',
                    'brand_name'        => $item['brand_name'],
                    'model'             => $item['model'],
                    'part_name'         => $part_map[$item['part_id']] ?? '',
                    'level_name'        => $item['level_name'],
                    'spec_name'         => $item['spec_name'],
                    'series_name'       => $item['series_name'],
                    'capacity'          => $item['capacity'],
                    'frequency'         => $item['frequency'],
                    'compatible_models' => implode(',', $compat_map[$item['id']] ?? []),
                    'praise_count'      => $item['praise_count'],
                    'sort'              => $item['sort'],
                    'created_at'        => $item['created_at'],
                    'updated_at'        => $item['updated_at']
                ];
            }

            // 推进游标，按主键继续取下一页
            $last_id = $items->last()->id;
        }
    }

    /**
     * 产品兼容性数据添加
     */
    public function save()
    {
        $post = request()->post([
            'type_id',
            'part_id',
            'model',
            'brand_name',
            'level_name',
            'spec_name',
            'series_name',
            'capacity',
            'frequency',
            'compatible_models',
            'sort',
            'disabled'
        ]);
        $data = array_merge($post, ['language_id' => request()->lang_id]);

        // 校验输入
        $validate = new ProductCompatValidate;
        if (!$validate->scene('add')->check($data)) {
            return error($validate->getError());
        }

        \think\facade\Db::startTrans();
        try {
            $compat = ProductCompatModel::create($data);
            if ($compat->isEmpty()) {
                throw new \Exception('操作失败');
            }

            $compatible_models = [];
            $models            = explode(',', str_replace('，', ',', $post['compatible_models']));
            foreach ($models as $model) {
                $compatible_models[] = [
                    'compat_id'         => $compat->id,
                    'compatible_model'  => trim($model),
                ];
            }
            if (empty($compatible_models)) {
                throw new \Exception('请确认兼容型号数据为有效');
            }
            ProductCompatModelsModel::insertAll($compatible_models);
        } catch (\Throwable $e) {
            \think\facade\Db::rollback();
            return error($e->getMessage());
        }

        \think\facade\Db::commit();
        return success('操作成功');
    }

    /**
     * 产品兼容性数据详细
     */
    public function read()
    {
        $id   = request()->param('id');
        $data = ProductCompatModel::withoutField(['language_id', 'deleted_at'])
            ->with([
                'compatible_models' => function ($query) {
                    $query->field(['compat_id', 'compatible_model']);
                }
            ])
            ->bypk($id)
            ->find();
        if (empty($data)) {
            return error('获取失败');
        }
        if (!empty($data->compatible_models)) {
            $compatible_models = [];
            foreach ($data->compatible_models as $model) {
                $compatible_models[] = $model['compatible_model'];
            }
            $data->compatible_models = implode(',', $compatible_models);
        }

        return success("获取成功", $data);
    }

    /**
     * 产品兼容性数据更新
     */
    public function update()
    {
        $id  = request()->param('id');
        $put = request()->put([
            'type_id',
            'part_id',
            'model',
            'brand_name',
            'level_name',
            'spec_name',
            'series_name',
            'capacity',
            'frequency',
            'compatible_models',
            'sort',
            'disabled'
        ]);
        $data = array_merge($put, ['id' => $id, 'language_id' => request()->lang_id]);

        // 校验输入
        $validate = new ProductCompatValidate;
        if (!$validate->scene('edit')->check($data)) {
            return error($validate->getError());
        }

        \think\facade\Db::startTrans();
        try {
            $compat = ProductCompatModel::bypk($id)->find();
            if (empty($compat)) {
                throw new \Exception('请确认操作对象是否正确');
            }
            if (!$compat->save($data)) {
                throw new \Exception('操作失败');
            }

            $compatible_models = [];
            $models            = explode(',', str_replace('，', ',', $put['compatible_models']));
            foreach ($models as $model) {
                $compatible_models[] = [
                    'compat_id'         => $compat->id,
                    'compatible_model'  => trim($model),
                ];
            }
            if (empty($compatible_models)) {
                throw new \Exception('请确认兼容型号数据为有效');
            }
            // 清除旧数据
            $deleted = ProductCompatModelsModel::destroy(function($query) use ($compat) {
                $query->where('compat_id', '=', $compat->id);
            });
            if (!$deleted) {
                throw new \Exception('删除兼容型号数据失败');
            }
            ProductCompatModelsModel::insertAll($compatible_models);
        } catch (\Throwable $e) {
            \think\facade\Db::rollback();
            return error($e->getMessage());
        }

        \think\facade\Db::commit();
        return success('操作成功');
    }

    /**
     * 产品兼容性数据删除
     */
    public function delete()
    {
        $id   = request()->param('id');
        $data = ProductCompatModel::bypk($id)->find();
        if (empty($data)) {
            return error("请确认操作对象是否正确");
        }

        if (!$data->delete()) {
            return error("操作失败");
        }

        return success("操作成功");
    }
}
