<?php
// +----------------------------------------------------------------------
// | onezan-admin 快速开发的管理平台
// +----------------------------------------------------------------------
// | 官方网址：https://www.onezan.cn
// +----------------------------------------------------------------------
// | onezan团队 版权所有 开源版本可自由商用
// +----------------------------------------------------------------------
// | Author: 543885307@qq.com
// +----------------------------------------------------------------------
namespace app\service\domain\admin;

use app\model\AdminUser;
use app\repository\admin\AdminUserRepository;
use app\service\domain\admin\CoreAdminPermissionService;
use app\service\AuthService;
use Core\service\BaseCoreService;
use think\facade\Cache;

/**
 * 管理员认证服务
 * @package app\service\domain\admin
 */
class CoreAdminAuthService extends BaseCoreService
{
    /**
     * 登录
     * @param string $username
     * @param string $password
     * @return array
     */
    public function login(string $username, string $password): array
    {
        if ($username === '' || $password === '') {
            return ['code' => 1, 'msg' => 'Unknown error'];
        }

        $failCacheKey = 'admin_login_fail:' . md5($username);
        $lockResult = $this->checkLoginLock($failCacheKey);
        if ($lockResult !== null) {
            return $lockResult;
        }

        $user = $this->adminUserRepository()->findByUsername($username);
        if (!$user || !password_verify($password, (string)$user->password)) {
            return $this->handleLoginFail($failCacheKey);
        }
        if ((int)($user->status ?? 0) !== 1) {
            return ['code' => 1, 'msg' => 'Account disabled'];
        }

        Cache::delete($failCacheKey);

        $token = AuthService::createToken((int)$user->id, 99);
        return [
            'code' => 0,
            'msg' => '',
            'data' => [
                'token' => $token,
                'user' => $this->buildProfileData($user),
            ],
        ];
    }

    /**
     * 获取用户信息
     * @param int $uid
     * @return array
     */
    public function profile(int $uid): array
    {
        if ($uid <= 0) {
            return ['code' => 401, 'msg' => 'Unauthorized', '_http_status' => 401];
        }

        $user = $this->adminUserRepository()->findById($uid);
        if (!$user) {
            return ['code' => 403, 'msg' => '', '_http_status' => 403];
        }

        return ['code' => 0, 'msg' => 'ok', 'data' => $this->buildProfileData($user)];
    }

    /**
     * 修改密码
     * @param int $uid
     * @param string $oldPassword
     * @param string $newPassword
     * @return array
     */
    public function changePassword(int $uid, string $oldPassword, string $newPassword): array
    {
        if ($uid <= 0) {
            return ['code' => 401, 'msg' => 'Unauthorized', '_http_status' => 401];
        }
        if ($oldPassword === '' || $newPassword === '') {
            return ['code' => 1, 'msg' => ''];
        }
        if (strlen($newPassword) < 6) {
            return ['code' => 1, 'msg' => 'Unknown error'];
        }

        $user = $this->adminUserRepository()->findById($uid);
        if (!$user) {
            return ['code' => 403, 'msg' => '', '_http_status' => 403];
        }
        if (!password_verify($oldPassword, (string)$user->password)) {
            return ['code' => 1, 'msg' => 'Unknown error'];
        }

        $user->password = password_hash($newPassword, PASSWORD_DEFAULT);
        $this->adminUserRepository()->save($user);

        return ['code' => 0, 'msg' => ''];
    }

    /**
     * 构建用户信息
     * @param AdminUser $user
     * @return array
     */
    private function buildProfileData(AdminUser $user): array
    {
        return [
            'id' => (int)$user->id,
            'username' => (string)$user->username,
            'nickname' => (string)($user->nickname ?? ''),
            'avatar' => (string)($user->avatar ?? ''),
            'permissions' => $this->permissionService()->getGrantedPermissionsForUser($user),
        ];
    }

    /**
     * 获取权限服务
     * @return CoreAdminPermissionService
     */
    private function permissionService(): CoreAdminPermissionService
    {
        return $this->app->make(CoreAdminPermissionService::class);
    }

    /**
     * 获取管理员用户仓库
     * @return AdminUserRepository
     */
    private function adminUserRepository(): AdminUserRepository
    {
        return $this->app->make(AdminUserRepository::class);
    }

    /**
     * 检查登录是否被锁定
     * @param string $cacheKey
     * @return array|null null = not locked, array = locked response
     */
    private function checkLoginLock(string $cacheKey): ?array
    {
        $failData = Cache::get($cacheKey);
        if (!is_array($failData)) {
            return null;
        }
        $lockedUntil = (int)($failData['locked_until'] ?? 0);
        if ($lockedUntil > 0 && time() < $lockedUntil) {
            $remain = $lockedUntil - time();
            return ['code' => 1, 'msg' => "Too many attempts, please wait {$remain} seconds"];
        }
        return null;
    }

    /**
     * 记录登录失败尝试并返回锁定信息
     * @param string $cacheKey
     * @return array
     */
    private function handleLoginFail(string $cacheKey): array
    {
        $maxAttempts = 5;
        $lockMinutes = 15;

        $failData = Cache::get($cacheKey);
        if (is_array($failData)) {
            $count = (int)($failData['count'] ?? 0);
        } else {
            $count = 0;
        }

        $count++;

        if ($count >= $maxAttempts) {
            $lockedUntil = time() + $lockMinutes * 60;
            Cache::set($cacheKey, [
                'count' => $count,
                'locked_until' => $lockedUntil,
            ], $lockMinutes * 60 * 2);
            return ['code' => 1, 'msg' => "Too many attempts, locked for {$lockMinutes} minutes"];
        }

        Cache::set($cacheKey, [
            'count' => $count,
            'locked_until' => 0,
        ], $lockMinutes * 60 * 2);

        $remaining = $maxAttempts - $count;
        return ['code' => 1, 'msg' => "Invalid credentials ({$remaining} attempts remaining)"];
    }
}
