<?php
declare(strict_types=1);

/**
 * 用户模型，对应 admin_user 表
 */
class Users extends Model
{
    protected string $table = 'admin_user';
    protected string $key = 'admin_id';

    /**
     * 部门表里实际存在的语言段，如 ['cn','en']；新增语言列后改这里即可
     */
    private const DEPA_NAME_LANGS = ['cn', 'en'];

    /**
     * 按当前语言取部门名称字段名，如 'admin_depa_cn_name' / 'admin_depa_en_name'
     */
    private function depaNameColumn(): string
    {
        return 'admin_depa_' . Lang::pickField(self::DEPA_NAME_LANGS) . '_name';
    }

    /**
     * 部门下拉列表，供前端 dxDataGrid 的部门列（多选标签框）做 lookup 用，名称按当前语言
     */
    public function listDepartments(): array
    {
        return $this->db->select('admin_depa', [
            'admin_depa_id',
            $this->depaNameColumn() . ' (admin_depa_name)',
        ]);
    }

    /**
     * grid() 查询时关联 admin_user_depa/admin_depa（部门）+ sys_admin_role/sys_role（角色），
     * 两个多对多关系一起 LEFT JOIN 会出现交叉重复行，GROUP_CONCAT 必须加 DISTINCT 去重
     */
    protected function gridFrom(): string
    {
        return '`admin_user` u'
            . ' LEFT JOIN `admin_user_depa` ud ON ud.`admin_user_depa_lnk_admin_id` = u.`admin_id`'
            . ' LEFT JOIN `admin_depa` d ON d.`admin_depa_id` = ud.`admin_user_depa_lnk_depa_id`'
            . ' LEFT JOIN `sys_admin_role` ar ON ar.`sys_admin_role_lnk_admin_id` = u.`admin_id`'
            . ' LEFT JOIN `sys_role` r ON r.`sys_role_id` = ar.`sys_admin_role_lnk_role_id`';
    }

    protected function gridSelect(): string
    {
        // MySQL 5.7.18 没有 JSON_ARRAYAGG，ID 列表先 GROUP_CONCAT 成逗号字符串，PHP 端再切成数组（见 grid()）
        return 'u.*,'
            . ' GROUP_CONCAT(DISTINCT d.`' . $this->depaNameColumn() . '` SEPARATOR \', \') AS `admin_depa_names`,'
            . ' GROUP_CONCAT(DISTINCT d.`admin_depa_id` SEPARATOR \',\') AS `admin_depa_ids`,'
            . ' GROUP_CONCAT(DISTINCT r.`sys_role_name` SEPARATOR \', \') AS `admin_role_names`,'
            . ' GROUP_CONCAT(DISTINCT r.`sys_role_id` SEPARATOR \',\') AS `admin_role_ids`';
    }

    protected function gridGroupBy(): string
    {
        return 'u.`admin_id`';
    }

    public function grid(array $loadOptions): array
    {
        $result = parent::grid($loadOptions);

        foreach ($result['data'] as &$row) {
            $row['admin_depa_ids'] = $this->csvToIntArray($row['admin_depa_ids'] ?? '');
            $row['admin_role_ids'] = $this->csvToIntArray($row['admin_role_ids'] ?? '');
        }

        return $result;
    }

    private function csvToIntArray(string $csv): array
    {
        return $csv !== '' ? array_map('intval', explode(',', $csv)) : [];
    }

    public function gridInsert(array $values): mixed
    {
        $depaIds = $this->extractIds($values, 'admin_depa_ids', 'admin_depa_names');
        $roleIds = $this->extractIds($values, 'admin_role_ids', 'admin_role_names');

        if (!empty($values['admin_password'])) {
            $values['admin_password'] = password_hash($values['admin_password'], PASSWORD_DEFAULT);
            $values['admin_password_update_time'] = date('Y-m-d H:i:s');
        }

        $adminId = parent::gridInsert($values);
        $this->bindDepartments((int) $adminId, $depaIds ?? []);
        if ($roleIds !== null) {
            (new Role($this->db))->bindToAdmin((int) $adminId, $roleIds);
        }

        return $adminId;
    }

    public function gridUpdate(mixed $key, array $values): int
    {
        $depaIds = $this->extractIds($values, 'admin_depa_ids', 'admin_depa_names');
        $roleIds = $this->extractIds($values, 'admin_role_ids', 'admin_role_names');

        if (!empty($values['admin_password'])) {
            $values['admin_password'] = password_hash($values['admin_password'], PASSWORD_DEFAULT);
            $values['admin_password_update_time'] = date('Y-m-d H:i:s');
        } else {
            unset($values['admin_password']); // 编辑时没填密码就不要把密码清空
        }

        $result = $values ? parent::gridUpdate($key, $values) : 0;

        if ($depaIds !== null) {
            $this->bindDepartments((int) $key, $depaIds);
        }
        if ($roleIds !== null) {
            (new Role($this->db))->bindToAdmin((int) $key, $roleIds);
        }

        return $result;
    }

    /**
     * 从 grid 提交的 values 里摘出多选标签框的值（ID 数组），
     * 同时把联表展示字段从 $values 里去掉，因为它们不属于 admin_user 表，不能直接写入
     * @return array<int>|null 没传这个字段就返回 null（表示这次提交不动这项绑定）
     */
    private function extractIds(array &$values, string $idsField, string $namesField): ?array
    {
        unset($values[$namesField]);

        if (!array_key_exists($idsField, $values)) {
            return null;
        }

        $ids = $values[$idsField];
        unset($values[$idsField]);

        return array_map('intval', is_array($ids) ? $ids : []);
    }

    /**
     * 全量替换某用户的部门绑定（多对多）
     */
    public function bindDepartments(int $adminId, array $depaIds): void
    {
        $this->db->delete('admin_user_depa', ['admin_user_depa_lnk_admin_id' => $adminId]);
        foreach (array_unique($depaIds) as $depaId) {
            if ($depaId > 0) {
                $this->db->insert('admin_user_depa', [
                    'admin_user_depa_lnk_admin_id' => $adminId,
                    'admin_user_depa_lnk_depa_id'  => $depaId,
                ]);
            }
        }
    }

    public function depaIdsOf(int $adminId): array
    {
        return array_map('intval', array_column(
            $this->db->select('admin_user_depa', ['admin_user_depa_lnk_depa_id'], ['admin_user_depa_lnk_admin_id' => $adminId]),
            'admin_user_depa_lnk_depa_id'
        ));
    }

    /**
     * 系统用户简单列表（id+用户名+姓名），供"角色与系统用户"页面用
     */
    public function listAll(): array
    {
        return $this->db->select('admin_user', ['admin_id', 'admin_username', 'admin_name'], ['ORDER' => ['admin_id' => 'ASC']]);
    }

    /**
     * 允许登录的系统用户列表（admin_allow=1），供业务表单里"添加人"这类用户下拉选择用
     * 额外附一个 admin_display_name（姓名为空时退回用户名），供 dxDataGrid 的 lookup.displayExpr 使用
     */
    public function listAllowed(): array
    {
        $rows = $this->db->select('admin_user', ['admin_id', 'admin_username', 'admin_name'], [
            'admin_allow' => 1,
            'ORDER'       => ['admin_name' => 'ASC'],
        ]);

        foreach ($rows as &$row) {
            $row['admin_display_name'] = $row['admin_name'] !== '' ? $row['admin_name'] : $row['admin_username'];
        }

        return $rows;
    }

    /**
     * 登录用，按用户名查一条完整记录（含密码哈希）
     */
    public function findByUsername(string $username): ?array
    {
        return $this->db->get('admin_user', '*', ['admin_username' => $username]) ?: null;
    }

    /**
     * 找回密码用，按用户名或邮箱查（任意一个匹配即可）
     */
    public function findByUsernameOrEmail(string $usernameOrEmail): ?array
    {
        return $this->db->get('admin_user', '*', [
            'OR' => [
                'admin_username' => $usernameOrEmail,
                'admin_email'    => $usernameOrEmail,
            ],
        ]) ?: null;
    }

    /**
     * 找回密码 / 用户自己改密码专用：直接设置新密码（不走 gridUpdate，不受 grid 编辑那套字段过滤影响）
     */
    public function updatePasswordById(int $adminId, string $plainPassword): void
    {
        $this->db->update('admin_user', [
            'admin_password'             => password_hash($plainPassword, PASSWORD_DEFAULT),
            'admin_password_update_time' => date('Y-m-d H:i:s'),
        ], ['admin_id' => $adminId]);
    }

    /**
     * 用户自己改资料专用：只允许改邮箱/手机号，跟后台 grid 编辑（能改任意字段）分开
     */
    public function updateProfile(int $adminId, string $email, string $mobile): void
    {
        $this->db->update('admin_user', [
            'admin_email'  => $email,
            'admin_mobile' => $mobile,
        ], ['admin_id' => $adminId]);
    }

    /**
     * 密码是否已经超过90天没改过，登录时用来提示用户重新修改密码
     */
    public function isPasswordStale(array $admin): bool
    {
        $updateTime = $admin['admin_password_update_time'] ?? null;

        return !$updateTime || strtotime($updateTime) < time() - 90 * 86400;
    }
}
