<?php
declare(strict_types=1);

/**
 * 角色模型，对应 sys_role 表
 */
class Role extends Model
{
    protected string $table = 'sys_role';
    protected string $key = 'sys_role_id';

    protected function gridFrom(): string
    {
        return '`sys_role` r LEFT JOIN `sys_site` s ON s.`sys_site_id` = r.`sys_role_lnk_site_id`';
    }

    protected function gridSelect(): string
    {
        return 'r.*, s.`sys_site_name`';
    }

    /**
     * 角色下拉列表，供前端多选标签框（角色绑定列）做 lookup 用
     * @param string|null $siteCode 传了就只要这个站点的角色（系统用户/部门绑定角色，只给 admin 站点用这个）
     * @param bool $excludeSiteCode true 时上面那个站点改成排除而不是只要（会员绑定角色：排除 admin 站点）
     */
    public function listAll(?string $siteCode = null, bool $excludeSiteCode = false): array
    {
        $where = [];
        if ($siteCode !== null) {
            $where['s.sys_site_code' . ($excludeSiteCode ? '[!]' : '')] = $siteCode;
        }

        return $this->db->select($this->table . ' (r)', [
            '[><]sys_site (s)' => ['r.sys_role_lnk_site_id' => 'sys_site_id'],
        ], ['r.sys_role_id', 'r.sys_role_name', 'r.sys_role_remark'], $where);
    }

    public function gridInsert(array $values): mixed
    {
        unset($values['sys_site_name']);
        return parent::gridInsert($values);
    }

    public function gridUpdate(mixed $key, array $values): int
    {
        unset($values['sys_site_name']);

        return parent::gridUpdate($key, $values);
    }

    /**
     * 该角色绑定的所有站点 admin 代码（用于校验会员不能绑 admin 站点角色等场景）
     */
    public function siteCode(int $roleId): ?string
    {
        $row = $this->db->select($this->table . ' (r)', [
            '[><]sys_site (s)' => ['r.sys_role_lnk_site_id' => 'sys_site_id'],
        ], ['s.sys_site_code'], ['r.sys_role_id' => $roleId]);

        return $row[0]['sys_site_code'] ?? null;
    }

    // ===== 角色-权限绑定 =====

    public function permissionsOf(int $roleId): array
    {
        return $this->db->select('sys_role_permission', '*', ['sys_role_permission_lnk_role_id' => $roleId]);
    }

    public function savePermission(int $roleId, string $code, array $types, int $dataScope, ?array $extraValues): void
    {
        $existing = $this->db->get('sys_role_permission', ['sys_role_permission_id'], [
            'sys_role_permission_lnk_role_id'         => $roleId,
            'sys_role_permission_lnk_permission_code' => $code,
        ]);

        $data = [
            'sys_role_permission_types'        => json_encode($types, JSON_UNESCAPED_UNICODE),
            'sys_role_permission_data_scope'   => $dataScope,
            'sys_role_permission_extra_values' => $extraValues !== null ? json_encode($extraValues, JSON_UNESCAPED_UNICODE) : null,
        ];

        if ($existing) {
            $this->db->update('sys_role_permission', $data, ['sys_role_permission_id' => $existing['sys_role_permission_id']]);
        } else {
            $data['sys_role_permission_lnk_role_id'] = $roleId;
            $data['sys_role_permission_lnk_permission_code'] = $code;
            $this->db->insert('sys_role_permission', $data);
        }
    }

    public function removePermission(int $roleId, string $code): void
    {
        $this->db->delete('sys_role_permission', [
            'sys_role_permission_lnk_role_id'         => $roleId,
            'sys_role_permission_lnk_permission_code' => $code,
        ]);
    }

    /**
     * 反查：拥有某个权限码授权的所有角色ID（用于"角色与权限"页面点权限高亮角色）
     */
    public function roleIdsGrantedCode(string $code): array
    {
        return array_column(
            $this->db->select('sys_role_permission', ['sys_role_permission_lnk_role_id'], ['sys_role_permission_lnk_permission_code' => $code]),
            'sys_role_permission_lnk_role_id'
        );
    }

    // ===== 用户/会员/部门 - 角色绑定（全量替换式） =====

    public function bindToAdmin(int $adminId, array $roleIds): void
    {
        $this->db->delete('sys_admin_role', ['sys_admin_role_lnk_admin_id' => $adminId]);
        foreach ($roleIds as $roleId) {
            $this->db->insert('sys_admin_role', [
                'sys_admin_role_lnk_admin_id' => $adminId,
                'sys_admin_role_lnk_role_id'  => $roleId,
            ]);
        }
    }

    public function roleIdsOfAdmin(int $adminId): array
    {
        return array_column(
            $this->db->select('sys_admin_role', ['sys_admin_role_lnk_role_id'], ['sys_admin_role_lnk_admin_id' => $adminId]),
            'sys_admin_role_lnk_role_id'
        );
    }

    /**
     * 反查：拥有某个角色的所有系统用户ID（用于"角色与系统用户"页面）
     */
    public function adminIdsOfRole(int $roleId): array
    {
        return array_column(
            $this->db->select('sys_admin_role', ['sys_admin_role_lnk_admin_id'], ['sys_admin_role_lnk_role_id' => $roleId]),
            'sys_admin_role_lnk_admin_id'
        );
    }

    /**
     * 会员绑角色，绑之前校验角色不属于 admin 站点
     */
    public function bindToMember(int $memberId, array $roleIds): void
    {
        foreach ($roleIds as $roleId) {
            if ($this->siteCode($roleId) === 'admin') {
                throw new MvcException('会员不能绑定 admin 站点的角色');
            }
        }

        $this->db->delete('sys_member_role', ['sys_member_role_lnk_member_id' => $memberId]);
        foreach ($roleIds as $roleId) {
            $this->db->insert('sys_member_role', [
                'sys_member_role_lnk_member_id' => $memberId,
                'sys_member_role_lnk_role_id'   => $roleId,
            ]);
        }
    }

    public function roleIdsOfMember(int $memberId): array
    {
        return array_column(
            $this->db->select('sys_member_role', ['sys_member_role_lnk_role_id'], ['sys_member_role_lnk_member_id' => $memberId]),
            'sys_member_role_lnk_role_id'
        );
    }

    public function bindToDepa(int $depaId, array $roleIds): void
    {
        $this->db->delete('sys_depa_role', ['sys_depa_role_lnk_depa_id' => $depaId]);
        foreach ($roleIds as $roleId) {
            $this->db->insert('sys_depa_role', [
                'sys_depa_role_lnk_depa_id' => $depaId,
                'sys_depa_role_lnk_role_id' => $roleId,
            ]);
        }
    }

    public function roleIdsOfDepa(int $depaId): array
    {
        return array_column(
            $this->db->select('sys_depa_role', ['sys_depa_role_lnk_role_id'], ['sys_depa_role_lnk_depa_id' => $depaId]),
            'sys_depa_role_lnk_role_id'
        );
    }
}
