<?php
/**
 * 芋头CMS
 * ============================================================================
 * 版权所有 2016-2028 沅陵县乌托邦网络服务有限公司，并保留所有权利。
 * 网站地址: http://www.yutoucms.cn
 * ----------------------------------------------------------------------------
 * 如果商业用途务必到官方购买正版授权, 以免引起不必要的法律纠纷.
 * ============================================================================
 * Author: 芋头 <mxgsf@qq.com>
 * Date: 2026-5-20
 */

namespace Core\Lib;

class License
{
    const VERSION = '1.0.0';
    const VERSION_CODE = 100;
    private $db;
    private $licenseCache = null;
    private $remoteChecked = false;

    const LEVEL_FREE = 1;
    const LEVEL_BASIC = 2;
    const LEVEL_PRO = 3;

    const FREE_PRODUCT_LIMIT = 5;
    const FREE_DOMAIN_COUNT = 1;
    const BASIC_DOMAIN_COUNT = 1;
    const PRO_DOMAIN_COUNT = 999;

    private $features = [
        self::LEVEL_FREE => [
            'basic_article', 'basic_product', 'basic_page', 'basic_image', 'basic_download',
            'guestbook', 'member', 'template_default'
        ],
        self::LEVEL_BASIC => [
            'basic_article', 'basic_product', 'basic_page', 'basic_image', 'basic_download',
            'guestbook', 'member', 'template_default',
            'comment', 'vote', 'slide', 'link', 'ads',
            'template_business', 'seo', 'backup'
        ],
        self::LEVEL_PRO => [
            'basic_article', 'basic_product', 'basic_page', 'basic_image', 'basic_download',
            'guestbook', 'member', 'template_default',
            'order_product', 'order_manage', 'comment', 'vote', 'slide', 'link', 'ads',
            'template_business', 'template_vip', 'seo', 'backup', 'restore',
            'payment_wechat', 'payment_alipay', 'auto_backup', 'priority_support',
            'online_upgrade', 'remove_copyright', 'recharge'
        ]
    ];

    public function __construct()
    {
        $this->db = \Core\Db\DB::getInstance();
    }

    public function getLocalLicense()
    {
        if ($this->licenseCache !== null) {
            return $this->licenseCache;
        }

        $sql = "SELECT * FROM {$this->db->table('license')} ORDER BY update_time DESC LIMIT 1";
        $license = $this->db->fetch($sql);
        $license = $this->ensureDomainLicense($license);
        $license = $this->ensureRemoteLicense($license);
        $this->licenseCache = $license;

        return $license;
    }

    public function getLevel()
    {
        $license = $this->getLocalLicense();
        return $license ? $license['license_level'] : self::LEVEL_FREE;
    }

    public function getLevelName($level = null)
    {
        $names = [1 => '免费版', 2 => '基础版', 3 => '专业版'];
        $l = $level !== null ? $level : $this->getLevel();
        return isset($names[$l]) ? $names[$l] : '免费版';
    }

    public function isAuthorized()
    {
        return $this->getLevel() >= self::LEVEL_BASIC;
    }

    public function isExpired()
    {
        $license = $this->getLocalLicense();
        if (!$license) return false;
        if (empty($license['license_expire']) || $license['license_expire'] === '0000-00-00') {
            return false;
        }
        return strtotime($license['license_expire']) < time();
    }

    public function getProductLimit()
    {
        $level = $this->getLevel();
        if ($level === self::LEVEL_FREE) {
            return self::FREE_PRODUCT_LIMIT;
        }
        return PHP_INT_MAX;
    }

    public function canAddProduct()
    {
        $level = $this->getLevel();
        if ($level === self::LEVEL_FREE) {
            $count = $this->db->fetch("SELECT COUNT(*) as cnt FROM {$this->db->table('product')}")['cnt'];
            return $count < self::FREE_PRODUCT_LIMIT;
        }
        return true;
    }

    public function hasFeature($feature)
    {
        $level = $this->getLevel();

        if ($level === self::LEVEL_FREE) {
            if (in_array($feature, ['payment_wechat', 'payment_alipay'])) {
                return false;
            }
            return in_array($feature, $this->features[self::LEVEL_FREE]);
        }

        if ($level === self::LEVEL_BASIC) {
            if (in_array($feature, ['payment_wechat', 'payment_alipay', 'auto_backup', 'priority_support', 'online_upgrade', 'remove_copyright', 'restore'])) {
                return false;
            }
            return in_array($feature, $this->features[self::LEVEL_BASIC]);
        }

        return in_array($feature, $this->features[self::LEVEL_PRO]);
    }

    public function canOnlineUpgrade()
    {
        return $this->getLevel() >= self::LEVEL_BASIC;
    }

    public function canRemoveCopyright()
    {
        return $this->getLevel() >= self::LEVEL_BASIC;
    }

    public function canInstallService()
    {
        return $this->getLevel() >= self::LEVEL_BASIC;
    }

    public function canUsePayment()
    {
        return $this->getLevel() >= self::LEVEL_PRO;
    }

    public function canUseOrder()
    {
        return $this->getLevel() >= self::LEVEL_BASIC;
    }

    public function getMaxDomains()
    {
        $level = $this->getLevel();
        switch ($level) {
            case self::LEVEL_FREE: return self::FREE_DOMAIN_COUNT;
            case self::LEVEL_BASIC: return self::BASIC_DOMAIN_COUNT;
            case self::LEVEL_PRO: return self::PRO_DOMAIN_COUNT;
            default: return self::FREE_DOMAIN_COUNT;
        }
    }

    public function checkCurrentDomain()
    {
        $currentDomain = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : '';
        $license = $this->getLocalLicense();
        if (!$license) return true;
        if ($this->getLevel() === self::LEVEL_FREE) {
            return $currentDomain === $license['license_domain'];
        }
        if (!empty($license['license_domain'])) {
            $domains = explode(',', $license['license_domain']);
            return in_array($currentDomain, $domains);
        }
        return true;
    }

    public function activate($licenseCode, $domain = '')
    {
        if (!$this->validateLicenseCode($licenseCode)) {
            return ['code' => 400, 'msg' => '授权码格式不正确'];
        }

        $licenseInfo = $this->fetchLicenseInfo($licenseCode);
        if (!$licenseInfo) {
            return ['code' => 400, 'msg' => '授权码无效或已过期'];
        }

        $currentDomain = $domain ? $domain : (isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : '');

        $data = [
            'license_code' => $licenseCode,
            'license_domain' => $currentDomain,
            'license_level' => $licenseInfo['level'],
            'license_expire' => isset($licenseInfo['expire']) ? $licenseInfo['expire'] : null,
            'site_id' => $this->generateSiteId(),
            'bind_count' => 1,
            'max_domains' => $this->getMaxDomainsByLevel($licenseInfo['level']),
            'license_status' => 1,
            'update_time' => time()
        ];

        $existing = $this->db->fetch("SELECT * FROM {$this->db->table('license')} ORDER BY update_time DESC LIMIT 1");
        if ($existing) {
            $this->db->update('license', $data, 'id = ?', [$existing['id']]);
        } else {
            $data['create_time'] = time();
            $this->db->insert('license', $data);
        }

        return ['code' => 200, 'msg' => '授权激活成功', 'data' => $data];
    }

    private function getMaxDomainsByLevel($level)
    {
        switch ($level) {
            case self::LEVEL_FREE: return self::FREE_DOMAIN_COUNT;
            case self::LEVEL_BASIC: return self::BASIC_DOMAIN_COUNT;
            case self::LEVEL_PRO: return self::PRO_DOMAIN_COUNT;
            default: return self::FREE_DOMAIN_COUNT;
        }
    }

    public function validateLicenseCode($code)
    {
        return strlen($code) >= 8;
    }

    public function generateSiteId()
    {
        return 'EC' . date('Ymd') . strtoupper(substr(md5(uniqid(mt_rand(), true)), 0, 8));
    }

    private function fetchLicenseInfo($licenseCode)
    {
        $licenseCenterUrl = 'http://yutou.wtbidc.cn/license-center';
        $remoteApi = $licenseCenterUrl . '/api.php';

        $postData = json_encode([
            'action' => 'check',
            'license_key' => $licenseCode,
            'domain' => isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : ''
        ]);

        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $remoteApi);
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_TIMEOUT, 10);
        curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
        $response = curl_exec($ch);
        curl_close($ch);

        $result = json_decode($response, true);
        if ($result && isset($result['code']) && intval($result['code']) === 200) {
            return [
                'level' => $result['level'],
                'expire' => isset($result['expire']) ? $result['expire'] : null
            ];
        }

        return null;
    }

    public function upgrade($targetLevel)
    {
        $current = $this->getLevel();
        if ($targetLevel <= $current) {
            return ['code' => 400, 'msg' => '不能降级'];
        }

        if ($current === self::LEVEL_FREE && $targetLevel > self::LEVEL_BASIC) {
            return ['code' => 400, 'msg' => '免费版需先升级到基础版'];
        }

        $license = $this->getLocalLicense();
        if ($license) {
            $this->db->update('license', [
                'license_level' => $targetLevel,
                'max_domains' => $this->getMaxDomainsByLevel($targetLevel),
                'update_time' => time()
            ], 'id = ?', [$license['id']]);
        }

        return ['code' => 200, 'msg' => '升级成功'];
    }

    public function getStatus()
    {
        $license = $this->getLocalLicense();
        $level = isset($license['license_level']) ? $license['license_level'] : self::LEVEL_FREE;

        return [
            'level' => $level,
            'level_name' => $this->getLevelName($level),
            'license_key' => isset($license['license_code']) ? $license['license_code'] : '',
            'expire' => isset($license['license_expire']) ? $license['license_expire'] : '',
            'expired' => $this->isExpired(),
            'authorized' => $level >= self::LEVEL_BASIC,
            'can_remove_copyright' => $level >= self::LEVEL_BASIC,
            'can_online_upgrade' => $level >= self::LEVEL_BASIC,
            'can_install_service' => $level >= self::LEVEL_BASIC,
            'can_use_payment' => $level >= self::LEVEL_PRO,
            'can_use_order' => $level >= self::LEVEL_BASIC,
            'product_limit' => $level === self::LEVEL_FREE ? self::FREE_PRODUCT_LIMIT : PHP_INT_MAX,
            'domain' => isset($license['license_domain']) ? $license['license_domain'] : (isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : ''),
            'site_id' => isset($license['site_id']) ? $license['site_id'] : '',
            'features' => isset($this->features[$level]) ? $this->features[$level] : []
        ];
    }

    public function getAvailableFeatures()
    {
        return isset($this->features[$this->getLevel()]) ? $this->features[$this->getLevel()] : $this->features[self::LEVEL_FREE];
    }

    public function disabledMessage($feature)
    {
        $level = $this->getLevel();

        if (in_array($feature, ['order_product', 'order_manage', 'recharge'])) {
            return '此功能需要专业版';
        }
        if (in_array($feature, ['payment_wechat', 'payment_alipay'])) {
            return '此功能需要专业版';
        }
        if ($feature === 'remove_copyright') {
            return '此功能需要专业版';
        }
        if ($feature === 'online_upgrade') {
            return '此功能需要基础版及以上版本';
        }

        return '当前版本不支持该功能';
    }

    public function initFreeLicense($domain)
    {
        $existing = $this->db->fetch("SELECT * FROM {$this->db->table('license')} ORDER BY update_time DESC LIMIT 1");
        if (!$existing) {
            $this->insertFreeLicense($domain);
        }
    }

    private function ensureDomainLicense($license)
    {
        $currentDomain = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : '';
        if ($currentDomain === '') {
            return $license;
        }

        if (!$license) {
            $this->insertFreeLicense($currentDomain);
            return $this->db->fetch("SELECT * FROM {$this->db->table('license')} ORDER BY update_time DESC LIMIT 1");
        }

        $matched = true;
        $licenseDomain = isset($license['license_domain']) ? trim($license['license_domain']) : '';
        $licenseLevel = isset($license['license_level']) ? intval($license['license_level']) : self::LEVEL_FREE;

        if ($licenseDomain !== '') {
            if ($licenseLevel <= self::LEVEL_FREE) {
                $matched = ($licenseDomain === $currentDomain);
            } else {
                $domains = array_filter(array_map('trim', explode(',', $licenseDomain)));
                $matched = in_array($currentDomain, $domains);
            }
        }

        if ($matched) {
            return $license;
        }

        $oldDomain = $licenseDomain;
        $this->db->query("DELETE FROM {$this->db->table('license')} WHERE 1=1");
        $this->insertFreeLicense($currentDomain);
        $this->resetTemplateConfigToDefault();

        if (session_status() === PHP_SESSION_ACTIVE) {
            $_SESSION['license_auto_reset_notice'] = [
                'old_domain' => $oldDomain,
                'new_domain' => $currentDomain,
                'time' => time(),
            ];
        }

        return $this->db->fetch("SELECT * FROM {$this->db->table('license')} ORDER BY update_time DESC LIMIT 1");
    }

    private function ensureRemoteLicense($license)
    {
        if ($this->remoteChecked) {
            return $license;
        }

        $this->remoteChecked = true;

        if (!$license) {
            return $license;
        }

        $licenseLevel = isset($license['license_level']) ? intval($license['license_level']) : self::LEVEL_FREE;
        $licenseCode = isset($license['license_code']) ? trim($license['license_code']) : '';

        if ($licenseLevel <= self::LEVEL_FREE || $licenseCode === '' || strpos($licenseCode, 'FREE-') === 0) {
            return $license;
        }

        $verifyResult = $this->verifyRemoteLicense($licenseCode);

        if (!$verifyResult['reachable']) {
            return $license;
        }

        if ($verifyResult['valid']) {
            $remoteLevel = isset($verifyResult['level']) ? intval($verifyResult['level']) : $licenseLevel;
            $remoteExpire = isset($verifyResult['expire']) ? $verifyResult['expire'] : null;
            $remoteDomain = isset($verifyResult['domain']) ? trim($verifyResult['domain']) : '';
            $updateData = [];

            if ($remoteLevel > 0 && $remoteLevel !== $licenseLevel) {
                $updateData['license_level'] = $remoteLevel;
                $updateData['max_domains'] = $this->getMaxDomainsByLevel($remoteLevel);
            }
            if ($remoteExpire !== (isset($license['license_expire']) ? $license['license_expire'] : null)) {
                $updateData['license_expire'] = $remoteExpire;
            }
            if ($remoteDomain !== '') {
                $localDomain = isset($license['license_domain']) ? trim($license['license_domain']) : '';
                if ($remoteDomain !== $localDomain) {
                    $updateData['license_domain'] = $remoteDomain;
                }
            }

            if (!empty($updateData) && isset($license['id'])) {
                $updateData['update_time'] = time();
                $this->db->update('license', $updateData, 'id = ?', [$license['id']]);
                $license = $this->db->fetch("SELECT * FROM {$this->db->table('license')} ORDER BY update_time DESC LIMIT 1");
            }

            return $license;
        }

        return $this->resetToFreeLicense($license, 'remote_invalid');
    }

    private function verifyRemoteLicense($licenseCode)
    {
        $licenseCenterUrl = 'http://yutou.wtbidc.cn/license-center';
        $remoteApi = $licenseCenterUrl . '/api.php?action=verify';
        $currentDomain = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : '';

        $postData = json_encode([
            'license_key' => $licenseCode,
            'domain' => $currentDomain
        ]);

        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $remoteApi);
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_TIMEOUT, 8);
        curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5);
        curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
        $response = curl_exec($ch);
        $curlErrno = curl_errno($ch);
        $httpCode = intval(curl_getinfo($ch, CURLINFO_HTTP_CODE));
        curl_close($ch);

        if ($response === false || $curlErrno) {
            return ['reachable' => false, 'valid' => false];
        }

        if ($httpCode !== 200) {
            return ['reachable' => false, 'valid' => false];
        }

        $result = json_decode($response, true);
        if (!is_array($result) || !isset($result['code'])) {
            return ['reachable' => false, 'valid' => false];
        }

        if (intval($result['code']) === 200) {
            return [
                'reachable' => true,
                'valid' => true,
                'level' => isset($result['level']) ? intval($result['level']) : self::LEVEL_FREE,
                'expire' => isset($result['expire']) ? $result['expire'] : null,
                'domain' => isset($result['domain']) ? $result['domain'] : ''
            ];
        }

        return [
            'reachable' => true,
            'valid' => false,
            'code' => intval($result['code']),
            'msg' => isset($result['msg']) ? $result['msg'] : ''
        ];
    }

    private function resetToFreeLicense($license, $reason)
    {
        $currentDomain = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : '';
        $oldDomain = isset($license['license_domain']) ? $license['license_domain'] : '';
        $oldKey = isset($license['license_code']) ? $license['license_code'] : '';

        $this->db->query("DELETE FROM {$this->db->table('license')} WHERE 1=1");
        $this->insertFreeLicense($currentDomain);
        $this->resetTemplateConfigToDefault();

        if (session_status() === PHP_SESSION_ACTIVE) {
            $_SESSION['license_remote_reset_notice'] = [
                'reason' => $reason,
                'old_domain' => $oldDomain,
                'new_domain' => $currentDomain,
                'old_key' => $oldKey,
                'time' => time(),
            ];
        }

        return $this->db->fetch("SELECT * FROM {$this->db->table('license')} ORDER BY update_time DESC LIMIT 1");
    }

    private function insertFreeLicense($domain)
    {
        $this->db->insert('license', [
            'license_code' => 'FREE-' . strtoupper(substr(md5($domain), 0, 8)),
            'license_domain' => $domain,
            'license_level' => self::LEVEL_FREE,
            'license_expire' => null,
            'site_id' => $this->generateSiteId(),
            'bind_count' => 1,
            'max_domains' => self::FREE_DOMAIN_COUNT,
            'license_status' => 1,
            'create_time' => time(),
            'update_time' => time()
        ]);
    }

    private function resetTemplateConfigToDefault()
    {
        $exists = $this->db->fetch("SELECT id FROM {$this->db->table('config')} WHERE config_key = 'site_template'");
        if ($exists) {
            $this->db->update('config', ['config_value' => 'default'], 'config_key = ?', ['site_template']);
        } else {
            $this->db->insert('config', [
                'config_key' => 'site_template',
                'config_value' => 'default',
                'config_type' => 'text',
                'config_group' => 'basic',
            ]);
        }

        $lastExists = $this->db->fetch("SELECT id FROM {$this->db->table('config')} WHERE config_key = 'last_template_key'");
        if ($lastExists) {
            $this->db->update('config', ['config_value' => 'default'], 'config_key = ?', ['last_template_key']);
        } else {
            $this->db->insert('config', [
                'config_key' => 'last_template_key',
                'config_value' => 'default',
                'config_type' => 'text',
                'config_group' => 'basic',
            ]);
        }
    }
}
