<?php

declare(strict_types=1);

namespace app\admin\controller\v1;

use app\admin\exception\InvalidOperateException;
use app\admin\model\v1\SysMenuAbilityPermissionModel;
use app\admin\model\v1\SysMenuModel;
use app\admin\validate\v1\SysMenuValidate;
use think\facade\Db;
use think\facade\Log;

/**
 * 菜单控制器
 */
class Menu
{
    // 菜单列表
    public function list()
    {
        $params = request()->param(['title' => '']);

        $menus = SysMenuModel::field([
                'id',
                'pid',
                'title'
            ])
            ->with(['menu_ability_permission'])
            ->withSearch(['title'], [
                'title' => $params['title'] ?? null
            ])
            ->enabled()
            ->order(['sort' => 'asc', 'id' => 'desc'])
            ->select()
            ->hidden(['menu_ability_permission.menu_id']);

        return success('获取成功', array_to_tree($menus->toArray(), 0, 'pid'));
    }

    // 分页数据
    public function index()
    {
        $params = request()->param(['title' => '']);

        $menus = SysMenuModel::field([
                'id',
                'pid',
                'icon',
                'title',
                'path',
                'component',
                'sort',
                'hidden',
                'status'
            ])
            ->withSearch(['title'], [
                'title' => $params['title'] ?? null
            ])
            ->order(['sort' => 'asc', 'id' => 'desc'])
            ->select();

        return success('获取成功', array_to_tree($menus->toArray(), 0, 'pid'));
    }

    // 详情数据
    public function read()
    {
        $id = request()->param('id');

        $menu = SysMenuModel::withoutField([
                'created_at',
                'updated_at',
                'deleted_at'
            ])
            ->with(['menu_ability_permission'])
            ->bypk($id)
            ->find()
            ->hidden(['menu_ability_permission.menu_id']);
        if (empty($menu)) {
            return error('菜单不存在');
        }

        return success('获取成功', $menu);
    }

    // 新增数据
    public function save()
    {
        $post = request()->post([
            'pid'                      => 0,
            'title',
            'name',
            'path',
            'icon',
            'redirect',
            'component',
            'hidden'                   => 0,
            'actived'                  => 0,
            'keep_alive'               => 0,
            'sort'                     => 0,
            'status'                   => 1,
            // $[*].ability_name 能力名称
            // $[*].permission 权限标志
            // $[*].sort 排序
            'menu_ability_permission' => '[]'
        ]);
        $menu_ability_permission = [];
        if (!empty($post['menu_ability_permission'])) {
            $menu_ability_permission = json_decode($post['menu_ability_permission'], true);
            unset($post['menu_ability_permission']);
        }

        $validate = new SysMenuValidate;
        $check_data = array_merge($post, ['menu_ability_permission' => $menu_ability_permission]);
        if (!$validate->scene('create')->check($check_data)) {
            return error($validate->getError());
        }

        SysMenuModel::startTrans();
        try {
            // 新增菜单
            $menu = SysMenuModel::create($post);
            if ($menu->isEmpty()) {
                throw new InvalidOperateException('新增菜单失败');
            }

            // 新增菜单能力权限
            if (!empty($menu_ability_permission)) {
                foreach ($menu_ability_permission as &$item) {
                    $item['menu_id'] = $menu->id;
                }
                unset($item);
                $permission = $menu->menuAbilityPermission()->saveAll($menu_ability_permission);
                if (empty($permission)) {
                    throw new InvalidOperateException('新增菜单能力权限失败');
                }
            }

            SysMenuModel::commit();
        } catch (InvalidOperateException $e) {
            SysMenuModel::rollback();
            return error($e->getMessage());
        } catch (\Throwable $th) {
            SysMenuModel::rollback();
            Log::error($th->getMessage());
            return error('操作失败');
        }

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

    // 更新数据
    public function update()
    {
        $id  = request()->param('id');
        $put = request()->put([
            'pid'                      => 0,
            'title',
            'name',
            'path',
            'icon',
            'redirect',
            'component',
            'hidden'                   => 0,
            'actived'                  => 0,
            'keep_alive'               => 0,
            'sort'                     => 0,
            'status'                   => 1,
            // $[*].ability_name 能力名称
            // $[*].permission 权限标志
            // $[*].sort 排序
            'menu_ability_permission' => '[]'
        ]);
        $menu_ability_permission = [];
        if (!empty($put['menu_ability_permission'])) {
            $menu_ability_permission = json_decode($put['menu_ability_permission'], true);
            unset($put['menu_ability_permission']);
        }

        $validate   = new SysMenuValidate;
        $check_data = array_merge($put, ['id' => $id, 'menu_ability_permission' => $menu_ability_permission]);
        if (!$validate->check($check_data)) {
            return error($validate->getError());
        }

        SysMenuModel::startTrans();
        try {
            $menu = SysMenuModel::bypk($id)->find();
            if ($menu->isEmpty()) {
                throw new InvalidOperateException('请确认操作对象是否存在');
            }
            // 更新菜单
            if (!$menu->save($put)) {
                throw new InvalidOperateException('更新菜单失败');
            }

            // 更新菜单能力权限
            $exists = SysMenuAbilityPermissionModel::menuId($menu->id)->count();
            if ($exists) {
                $deleted = SysMenuAbilityPermissionModel::menuId($menu->id)->delete();
                if (!$deleted) {
                    throw new InvalidOperateException('删除旧菜单能力权限失败');
                }
            }
            if (!empty($menu_ability_permission)) {
                foreach ($menu_ability_permission as &$item) {
                    $item['menu_id'] = $menu->id;
                }
                unset($item);

                $permission = (new SysMenuAbilityPermissionModel)->saveAll($menu_ability_permission);
                if (empty($permission)) {
                    throw new InvalidOperateException('更新菜单能力权限失败');
                }
            }

            SysMenuModel::commit();
        } catch (InvalidOperateException $e) {
            SysMenuModel::rollback();
            return error($e->getMessage());
        } catch (\Throwable $th) {
            SysMenuModel::rollback();
            Log::error($th->getMessage());
            return error('操作失败');
        }

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

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

        // 读取文件
        $keys_map = [
            'A' => 'id',
            'B' => 'pid',
            'C' => 'title',
            'D' => 'name',
            'E' => 'path',
            'F' => 'icon',
            'G' => 'redirect',
            'H' => 'component',
            'I' => 'hidden',
            'J' => 'actived',
            'K' => 'keep_alive',
            'L' => 'sort',
            'M' => 'status',
            'N' => 'menu_ability_permission'
        ];
        $xlsx_data = xlsx_reader($file->getRealPath(), 2, $keys_map);
        if (empty($xlsx_data)) {
            return success('操作成功');
        }
        
        // 处理行序号，防止后续被打乱
        foreach ($xlsx_data as $row => &$item) {
            $item['seq_no'] = $row;
        }
        unset($item);
        
        // 数据组装成树形结构，方便处理上下级关系
        $xlsx_data_tree = array_to_tree($xlsx_data, 0, 'pid');
        // 处理导入菜单数据
        $handle_errors = $this->handleImportData($xlsx_data_tree);
        if (!empty($handle_errors)) {
            return error(implode(PHP_EOL, $handle_errors));
        }
        return success('操作成功');
    }
    // 处理导入数据
    private function handleImportData($menus)
    {
        $errors     = [];
        $menu_model = new SysMenuModel;
        $chunks     = array_chunk($menus, 100, true);
        foreach ($chunks as $chunk) {
            // 执行保存
            if (!$this->executeReplaceMenu($chunk)) {
                $errors[] = sprintf('第【%s】行保存失败', implode(',', array_column($chunk, 'seq_no')));
                continue;
            }

            // 获取该批次菜单，以获取新增情况的菜单的id
            $menus_map = $menu_model->whereIn('name', array_column($chunk, 'name'))->column('id', 'name');
            foreach ($chunk as &$it) {
                // 更改菜单的子菜单pid值为新值
                if (!empty($it['children'])) {
                    foreach ($it['children'] as &$child) {
                        if (isset($menus_map[$child['name']])) {
                            $child['pid'] = $menus_map[$child['name']];
                        }
                    }
                    unset($child);
                }

                // 更改菜单能力权限的menu_id
                if (!empty($it['menu_ability_permission'])) {
                    $menu_ability_permission = json_decode($it['menu_ability_permission'], true);
                    foreach ($menu_ability_permission as &$permission) {
                        if (isset($menus_map[$it['name']])) {
                            $permission['menu_id'] = $menus_map[$it['name']];
                        }
                    }
                    unset($permission);
                    $it['menu_ability_permission'] = $menu_ability_permission;
                }
            }
            unset($it);

            // 处理子菜单
            $childrens     = array_reduce(array_column($chunk, 'children'), 'array_merge', []);
            $handle_errors = $this->handleImportData($childrens);
            if (!empty($handler_ret)) {
                $errors = array_merge($errors, $handle_errors);
            }

            // 更新菜单能力权限
            $menu_ability_permissions = array_reduce(array_column($chunk, 'menu_ability_permission'), 'array_merge', []);
            if (!empty($menu_ability_permissions)) {
                // 菜单能力权限模型实例
                $ability_permission_model = new SysMenuAbilityPermissionModel;
                // 删除旧的菜单能力权限
                $menus_id = array_column($menu_ability_permissions, 'menu_id');
                $exists   = $ability_permission_model->whereIn('menu_id', $menus_id)->count();
                if ($exists) {
                    $deleted = $ability_permission_model->whereIn('menu_id', $menus_id)->delete();
                    if (!$deleted) {
                        $errors[] = sprintf('第【%s】行删除旧菜单能力权限失败', implode(',', array_column($chunk, 'seq_no')));
                        continue;
                    }
                }
                // 保存新的菜单能力权限
                $ability_updated = $ability_permission_model->saveAll($menu_ability_permissions);
                if ($ability_updated->isEmpty()) {
                    $errors[] = sprintf('第【%s】行保存新菜单能力权限失败', implode(',', array_column($chunk, 'seq_no')));
                    continue;
                }
            }
        }

        return $errors;
    }
    // 组装repalce语句的values
    private function buildReplaceMenuValues($menu)
    {
        return sprintf(
            '(%d, %d, "%s", "%s", "%s", "%s", "%s", "%s", %d, %d, %d, %d, %d)',
            $menu['id'],
            $menu['pid'],
            $menu['title'],
            $menu['name'],
            $menu['path'],
            $menu['icon'],
            $menu['redirect'],
            $menu['component'],
            $menu['hidden'],
            $menu['actived'],
            $menu['keep_alive'],
            $menu['sort'],
            $menu['status']
        );
    }
    // 执行repalce语句
    private function executeReplaceMenu($menus)
    {
        // 获取已存在的菜单
        $menus_map = SysMenuModel::whereIn('name', array_column($menus, 'name'))->column('id', 'name');

        // 组装保存sql语句
        $values = [];
        foreach ($menus as &$it) {
            if (isset($menus_map[$it['name']])) {
                $it['id'] = $menus_map[$it['name']];
            }
            if ($it['level'] == 1) {
                $it['pid'] = 0;
            }
            $values[] = $this->buildReplaceMenuValues($it);
        }
        unset($it);

        return Db::execute(
            sprintf(
                'REPLACE INTO %s (
                    `id`, 
                    `pid`, 
                    `title`, 
                    `name`, 
                    `path`, 
                    `icon`, 
                    `redirect`, 
                    `component`, 
                    `hidden`, 
                    `actived`, 
                    `keep_alive`, 
                    `sort`, 
                    `status`
                ) VALUES %s;',
                (new SysMenuModel)->getTable(),
                implode(',', $values)
            )
        );
    }

    // 导出数据
    public function export()
    {
        $schema = [
            'id'                      => '菜单ID',
            'pid'                     => '父级ID',
            'title'                   => '菜单标题',
            'name'                    => '菜单名称',
            'path'                    => '菜单路径',
            'icon'                    => '菜单图标',
            'redirect'                => '菜单重定向路径',
            'component'               => '菜单组件路径',
            'hidden'                  => '是否隐藏',
            'actived'                 => '是否激活',
            'keep_alive'              => '是否缓存',
            'sort'                    => '排序',
            'status'                  => '状态',
            'menu_ability_permission' => '菜单能力权限'
        ];

        // 获取导出数据
        $data = $this->getExportMenuData();

        // 导出
        return xlsx_writer($data, $schema, '菜单列表' . date('YmdHis'), 'php://output', 'file');
    }
    private function getExportMenuData()
    {
        $param = request()->param(['title' => '']);

        $menus = SysMenuModel::withoutField([
                'created_at',
                'updated_at',
                'deleted_at'
            ])
            ->with(['menu_ability_permission'])
            ->withSearch(['title'], [
                'title' => $param['title'] ?? null
            ])
            ->order(['id' => 'asc'])
            ->select()
            ->hidden(['menu_ability_permission.menu_id']);
        if (!$menus->isEmpty()) {
            $menus->each(function ($item) {
                $item->menu_ability_permission = json_encode($item->menu_ability_permission);
            });
        }

        return $menus;
    }

    // 设置排序值
    public function sort()
    {
        $id   = request()->param('id');
        $sort = request()->post('sort');

        $menu = SysMenuModel::bypk($id)->find();
        if ($menu->isEmpty()) {
            return error('请确认操作对象是否存在');
        }

        $menu->sort = $sort;
        if (!$menu->save()) {
            return error('操作失败');
        }

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

    // 删除数据
    public function delete()
    {
        $id = request()->param('id');

        SysMenuModel::startTrans();
        try {
            $menu = SysMenuModel::bypk($id)->find();
            if ($menu->isEmpty()) {
                throw new InvalidOperateException('请确认操作对象是否存在');
            }
            if (!$menu->delete()) {
                throw new InvalidOperateException('删除菜单失败');
            }

            SysMenuModel::commit();
        } catch (InvalidOperateException $e) {
            SysMenuModel::rollback();
            return error($e->getMessage());
        } catch (\Throwable $th) {
            SysMenuModel::rollback();
            Log::error($th->getMessage());
            return error('操作失败');
        }

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