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

use app\repository\user\UserLevelRepository;
use app\repository\user\UserRepository;
use app\service\EmailService;
use app\service\IdService;
use Core\service\BaseCoreService;
use app\service\domain\security\CoreSecurityFilterService;
use think\facade\Db;
use think\facade\Cache;
use think\facade\Log;

/**
 * 用户服务
 * Class CoreUserService
 * @package app\service\domain\user
 */
class CoreUserService extends BaseCoreService
{
    private const EMAIL_CODE_TTL = 300;
    private const EMAIL_RATE_TTL = 60;

    /**
     * 确保用户有邀请码
     * @param int $userId 用户ID
     * @return string
     */
    public function ensureInviteCode(int $userId): string
    {
        $current = $this->userRepository()->getInviteCode($userId);
        if ($current !== '') {
            return $current;
        }

        $code = IdService::generateUniqueInviteCode();
        $this->userRepository()->updateInviteCode($userId, $code);
        return $code;
    }

    /**
     * 设置用户等级
     * @param int $userId 用户ID
     * @param int $levelId 等级ID
     * @return array
     */
    public function setLevel(int $userId, int $levelId): array
    {
        $user = $this->userRepository()->findById($userId);
        if (!$user) {
            return ['code' => 1, 'msg' => ''];
        }

        $level = $this->userLevelRepository()->findById($levelId);
        if (!$level || (int)($level->status ?? 0) !== 1) {
            return ['code' => 1, 'msg' => ''];
        }

        $user->level_id = (int)$level->id;
        $user->save();
        return ['code' => 0, 'msg' => ''];
    }

    /**
     * 重置用户密码
     * @param int $userId 用户ID
     * @param string $password 新密码
     * @return array
     */
    public function resetPassword(int $userId, string $password): array
    {
        if ($password === '') {
            return ['code' => 1, 'msg' => ''];
        }
        if (mb_strlen($password) < 6) {
            return ['code' => 1, 'msg' => 'Password must be at least 6 characters'];
        }

        $user = $this->userRepository()->findById($userId);
        if (!$user) {
            return ['code' => 1, 'msg' => ''];
        }

        $user->password = password_hash($password, PASSWORD_BCRYPT);
        $user->save();
        return ['code' => 0, 'msg' => ''];
    }

    /**
     * 绑定用户邀请码
     * @param int $userId 用户ID
     * @param string $inviteCode 邀请码
     * @return array
     */
    public function bindByInviteCode(int $userId, string $inviteCode): array
    {
        $inviteCode = trim($inviteCode);
        if ($inviteCode === '') {
            return ['code' => 1, 'msg' => ''];
        }

        $current = $this->userRepository()->findById($userId);
        if (!$current) {
            return ['code' => 1, 'msg' => ''];
        }

        if ((int)$current->parent_user_id > 0) {
            return ['code' => 1, 'msg' => ''];
        }

        $parent = $this->userRepository()->findByInviteCode($inviteCode);
        if (!$parent) {
            return ['code' => 1, 'msg' => ''];
        }

        if ((int)$parent->id === $userId) {
            return ['code' => 1, 'msg' => ''];
        }

        $this->userRepository()->bindParent($userId, (int)$parent->id);
        return ['code' => 0, 'msg' => ''];
    }

    /**
     * 更新用户个人信息
     * @param int $userId 用户ID
     * @param array $payload 更新数据
     * @return array
     */
    public function updateProfile(int $userId, array $payload): array
    {
        $user = $this->userRepository()->lockById($userId);
        if (!$user || (int)($user->status ?? 0) !== 1) {
            return ['code' => 1, 'msg' => ''];
        }

        $nickname = trim((string)($payload['nickname'] ?? ''));
        $avatar = trim((string)($payload['avatar'] ?? ''));

        if ($nickname !== '') {
            $securityResult = $this->securityFilter()->filterText($nickname);
            if ((int)($securityResult['code'] ?? 1) !== 0) {
                return ['code' => 1, 'msg' => (string)($securityResult['msg'] ?? '')];
            }
            $user->nickname = mb_substr($nickname, 0, 20);
        }

        if ($avatar !== '') {
            $user->avatar = mb_substr($avatar, 0, 255);
        }

        $user->save();
        return ['code' => 0, 'msg' => ''];
    }

    /**
     * 取消用户账号
     * @param int $userId 用户ID
     * @return array
     */
    public function cancelAccount(int $userId): array
    {
        Db::startTrans();
        try {
            $user = $this->userRepository()->lockById($userId);
            if (!$user) {
                throw new \RuntimeException('');
            }

            $cancelledUsername = 'cancelled_' . $userId . '_' . time();
            $user->username = mb_substr($cancelledUsername, 0, 50);
            $user->nickname = '';
            $user->avatar = '';
            $user->email = '';
            $user->phone = '';
            $user->gender = 0;
            $user->birthday = null;
            $user->status = 0;
            $user->level_id = 1;
            $user->parent_user_id = 0;
            $user->invite_code = '';
            $user->balance = 0;
            $user->points = 0;
            $user->verify_status = 0;
            $user->extends = [];
            $user->password = password_hash(bin2hex(random_bytes(8)), PASSWORD_BCRYPT);
            $user->delete_time = date('Y-m-d H:i:s');
            $user->save();

            Db::name('sys_user_addresses')->where('user_id', $userId)->delete();
            Db::name('sys_user_channel_accounts')->where('user_id', $userId)->delete();
            Db::name('sys_user_verify_profiles')->where('user_id', $userId)->delete();

            Db::commit();
            return ['code' => 0, 'msg' => ''];
        } catch (\Throwable $e) {
            Db::rollback();
            return ['code' => 1, 'msg' => $e->getMessage()];
        }
    }

    /**
     * 发送邮箱验证码
     * @param int $userId 用户ID
     * @param string $email 邮箱
     * @return array
     */
    public function sendEmailCode(int $userId, string $email): array
    {
        $email = trim($email);
        if ($email === '' || !filter_var($email, FILTER_VALIDATE_EMAIL)) {
            return ['code' => 1, 'msg' => ''];
        }

        $user = $this->userRepository()->findById($userId);
        if (!$user || (int)($user->status ?? 0) !== 1) {
            return ['code' => 1, 'msg' => ''];
        }

        $rateKey = $this->emailRateKey($userId, $email);
        if (Cache::has($rateKey)) {
            $remain = Cache::get($rateKey, self::EMAIL_RATE_TTL);
            return ['code' => 1, 'msg' => '' . max(1, (int)$remain) . ''];
        }

        $code = (string)random_int(100000, 999999);
        Cache::set($this->emailCodeKey($userId, $email), $code, self::EMAIL_CODE_TTL);
        Cache::set($rateKey, self::EMAIL_RATE_TTL, self::EMAIL_RATE_TTL);

        try {
            EmailService::send(
                $email,
                '',
                '<p><strong>' . $code . '</strong></p><p>??/p>'
            );
        } catch (\Throwable $e) {
            Cache::delete($this->emailCodeKey($userId, $email));
            Cache::delete($rateKey);
            return ['code' => 1, 'msg' => $e->getMessage()];
        }

        if (app()->isDebug()) {
            Log::info('[] ? ' . $code . ' -> ' . $email);
        }

        return ['code' => 0, 'msg' => '', 'data' => ['ttl' => self::EMAIL_CODE_TTL]];
    }

    /**
     * 绑定用户邮箱
     * @param int $userId 用户ID
     * @param string $email 邮箱
     * @param string $code 验证码
     * @return array
     */
    public function bindEmail(int $userId, string $email, string $code): array
    {
        $email = trim($email);
        $code = trim($code);
        if ($email === '' || !filter_var($email, FILTER_VALIDATE_EMAIL)) {
            return ['code' => 1, 'msg' => ''];
        }
        if (!preg_match('/^\d{6}$/', $code)) {
            return ['code' => 1, 'msg' => ''];
        }

        $cachedCode = Cache::get($this->emailCodeKey($userId, $email));
        if ($cachedCode === null || $cachedCode === '') {
            return ['code' => 1, 'msg' => ''];
        }
        if ((string)$cachedCode !== $code) {
            return ['code' => 1, 'msg' => ''];
        }

        $user = $this->userRepository()->lockById($userId);
        if (!$user || (int)($user->status ?? 0) !== 1) {
            return ['code' => 1, 'msg' => ''];
        }

        $user->email = mb_substr($email, 0, 100);
        $user->save();

        Cache::delete($this->emailCodeKey($userId, $email));
        Cache::delete($this->emailRateKey($userId, $email));

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

    /**
     * 获取用户仓储实例
     * @return UserRepository
     */
    private function userRepository(): UserRepository
    {
        return $this->app->make(UserRepository::class);
    }


    /**
     * 获取用户等级仓储实例
     * @return UserLevelRepository
     */
    private function userLevelRepository(): UserLevelRepository
    {
        return $this->app->make(UserLevelRepository::class);
    }

    /**
     * 获取安全过滤服务实例
     * @return CoreSecurityFilterService
     */
    private function securityFilter(): CoreSecurityFilterService
    {
        return $this->app->make(CoreSecurityFilterService::class);
    }

    /**
     * 生成邮箱验证码缓存键
     * @param int $userId 用户ID
     * @param string $email 邮箱地址
     * @return string 缓存键
     */
    private function emailCodeKey(int $userId, string $email): string
    {
        return 'user_email_code:' . $userId . ':' . md5(strtolower($email));
    }

    /**
     * 生成邮箱发送频率限制缓存键
     * @param int $userId 用户ID
     * @param string $email 邮箱地址
     * @return string 缓存键
     */
    private function emailRateKey(int $userId, string $email): string
    {
        return 'user_email_rate:' . $userId . ':' . md5(strtolower($email));
    }
}