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

namespace Core\Lib;

if (!class_exists('\\Core\\Db\\DB', false)) {
    require_once dirname(__DIR__) . '/db/db.php';
}

class License
{
    const VERSION = '1.1.6';
    const VERSION_CODE = 116;
    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 $db;
    private $licenseCache = null;
    private $remoteChecked = false;
    private $installReportAttempted = false;

    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;
        }

        if (!$this->hasLicenseTable()) {
            $this->licenseCache = $this->buildDefaultLicense();
            return $this->licenseCache;
        }

        $row = $this->db->fetch("SELECT * FROM " . $this->db->table('license') . " WHERE license_level > 1 AND license_status = 1 ORDER BY id DESC LIMIT 1");
        if (!$row) {
            $row = $this->db->fetch("SELECT * FROM " . $this->db->table('license') . " ORDER BY id DESC LIMIT 1");
        }
        $row = $this->ensureLocalLicenseRecord($row);
        if (!$row) {
            $this->licenseCache = $this->buildDefaultLicense();
            return $this->licenseCache;
        }

        $row['license_level'] = intval($row['license_level'] ?? self::LEVEL_FREE);
        $row['max_domains'] = intval($row['max_domains'] ?? self::FREE_DOMAIN_COUNT);
        $row['bind_count'] = intval($row['bind_count'] ?? 1);
        $row['license_status'] = intval($row['license_status'] ?? 1);
        $row['version'] = self::VERSION;
        $row['version_code'] = self::VERSION_CODE;
        $row['features'] = $this->features[$row['license_level']] ?? $this->features[self::LEVEL_FREE];
        $row['is_expired'] = !empty($row['license_expire']) && strtotime((string) $row['license_expire']) < time();
        $row['is_valid'] = $row['license_status'] == 1 && !$row['is_expired'];
        $row['auth_mode'] = $this->resolveAuthMode($row);

        $this->licenseCache = $row;
        $this->maybeReportInstallation($this->licenseCache);
        return $this->licenseCache;
    }

    public function getStatus()
    {
        $license = $this->getLocalLicense();

        return [
            'code' => $license['license_code'] ?? '',
            'license_key' => $license['license_code'] ?? '',
            'license_code' => $license['license_code'] ?? '',
            'domain' => $license['license_domain'] ?? '',
            'site_id' => $license['site_id'] ?? '',
            'level' => intval($license['license_level'] ?? self::LEVEL_FREE),
            'level_name' => $this->getLevelName(),
            'expire' => $license['license_expire'] ?? null,
            'bind_count' => intval($license['bind_count'] ?? 1),
            'max_domains' => intval($license['max_domains'] ?? self::FREE_DOMAIN_COUNT),
            'status' => intval($license['license_status'] ?? 1),
            'features' => $license['features'] ?? $this->features[self::LEVEL_FREE],
            'is_expired' => !empty($license['is_expired']),
            'is_valid' => !empty($license['is_valid']),
            'auth_mode' => $license['auth_mode'] ?? 'local',
            'version' => self::VERSION,
            'version_code' => self::VERSION_CODE,
            'can_use_builder' => $this->canUseBuilder(),
            'can_use_order' => $this->canUseOrder(),
            'can_use_payment' => $this->canUsePayment(),
            'can_online_upgrade' => $this->canOnlineUpgrade(),
            'can_remove_copyright' => $this->canRemoveCopyright(),
        ];
    }

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

    public function getLevelName()
    {
        $level = $this->getLevel();
        if ($level >= self::LEVEL_PRO) {
            return '专业版';
        }
        if ($level >= self::LEVEL_BASIC) {
            return '基础版';
        }
        return '免费版';
    }

    public function isAuthorized()
    {
        return !empty($this->getLocalLicense()['is_valid']);
    }

    public function hasFeature($feature)
    {
        $license = $this->getLocalLicense();
        $features = $license['features'] ?? $this->features[self::LEVEL_FREE];
        return in_array((string) $feature, $features, true);
    }

    public function canUseOrder()
    {
        return $this->hasFeature('order_manage') || $this->hasFeature('order_product');
    }

    public function canUsePayment()
    {
        return $this->hasFeature('payment_wechat') || $this->hasFeature('payment_alipay') || $this->hasFeature('recharge');
    }

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

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

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

    public function reportInstallation($force = false, $source = 'runtime')
    {
        $license = $this->getLocalLicense();
        return $this->maybeReportInstallation($license, (bool) $force, (string) $source);
    }

    public function canAddProduct()
    {
        if ($this->getLevel() > self::LEVEL_FREE) {
            return true;
        }

        try {
            $row = $this->db->fetch("SELECT COUNT(*) AS total FROM " . $this->db->table('product'));
            $count = intval($row['total'] ?? 0);
            return $count < self::FREE_PRODUCT_LIMIT;
        } catch (\Throwable $e) {
            return true;
        }
    }

    public function disabledMessage($feature = '')
    {
        if ($feature !== '') {
            return $feature . ' 功能当前不可用，请升级到专业版后操作。';
        }
        return '当前版本无此操作权限，请升级到专业版后使用。';
    }

    private function resolveAuthMode(array $license)
    {
        $remoteEnabled = $this->isRemoteAuthEnabled();
        if (!$remoteEnabled) {
            return 'local';
        }
        return !empty($license['site_id']) ? 'remote' : 'local';
    }

    private function isRemoteAuthEnabled()
    {
        return true;
    }

    private function hasLicenseTable()
    {
        try {
            $this->db->query("SELECT 1 FROM " . $this->db->table('license') . " LIMIT 1");
            return true;
        } catch (\Throwable $e) {
            return false;
        }
    }

    private function buildDefaultLicense()
    {
        return [
            'license_code' => 'FREE-' . strtoupper(substr(md5('default'), 0, 8)),
            'license_domain' => $_SERVER['HTTP_HOST'] ?? 'localhost',
            'license_level' => self::LEVEL_FREE,
            'license_expire' => null,
            'site_id' => '',
            'bind_count' => 1,
            'max_domains' => self::FREE_DOMAIN_COUNT,
            'license_status' => 1,
            'create_time' => time(),
            'update_time' => time(),
            'version' => self::VERSION,
            'version_code' => self::VERSION_CODE,
            'features' => $this->features[self::LEVEL_FREE],
            'is_expired' => false,
            'is_valid' => true,
            'auth_mode' => 'local',
        ];
    }

    private function ensureLocalLicenseRecord($row = null)
    {
        if (!$this->hasLicenseTable()) {
            return null;
        }

        $now = time();
        $domain = $this->getCurrentHost();

        if (!$row) {
            $row = [
                'license_code' => 'FREE-' . strtoupper(substr(md5($domain !== '' ? $domain : 'localhost'), 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' => $now,
                'update_time' => $now,
            ];
            $row['id'] = $this->db->insert('license', $row);
            return $row;
        }

        $updateData = [];
        if (empty($row['site_id'])) {
            $updateData['site_id'] = $this->generateSiteId();
        }
        if (empty($row['license_code'])) {
            $updateData['license_code'] = 'FREE-' . strtoupper(substr(md5($domain !== '' ? $domain : 'localhost'), 0, 8));
        }
        if ($domain !== '' && empty($row['license_domain'])) {
            $updateData['license_domain'] = $domain;
        }
        if (intval($row['bind_count'] ?? 0) < 1) {
            $updateData['bind_count'] = 1;
        }
        if (intval($row['max_domains'] ?? 0) < 1) {
            $updateData['max_domains'] = intval($row['license_level'] ?? self::LEVEL_FREE) >= self::LEVEL_PRO
                ? self::PRO_DOMAIN_COUNT
                : self::FREE_DOMAIN_COUNT;
        }
        if (!array_key_exists('license_status', $row) || $row['license_status'] === null || $row['license_status'] === '') {
            $updateData['license_status'] = 1;
        }

        if (!empty($updateData) && !empty($row['id'])) {
            $updateData['update_time'] = $now;
            $this->db->update('license', $updateData, 'id = ?', [$row['id']]);
            $row = array_merge($row, $updateData);
        }

        return $row;
    }

    private function maybeReportInstallation(array $license, $force = false, $source = 'runtime')
    {
        if ($this->installReportAttempted && !$force) {
            return false;
        }
        $this->installReportAttempted = true;

        if (!function_exists('curl_init')) {
            return false;
        }

        $siteId = trim((string) ($license['site_id'] ?? ''));
        $domain = trim((string) ($license['license_domain'] ?? $this->getCurrentHost()));
        if ($siteId === '' || $domain === '') {
            return false;
        }

        $cache = $this->loadInstallReportCache();
        $cacheKey = $siteId;
        $lastSentAt = intval($cache[$cacheKey]['time'] ?? 0);
        $lastDomain = (string) ($cache[$cacheKey]['domain'] ?? '');
        $lastLevel = intval($cache[$cacheKey]['level'] ?? 0);
        if (
            !$force
            && $lastSentAt > 0
            && ($lastSentAt + 86400) > time()
            && $lastDomain === $domain
            && $lastLevel === intval($license['license_level'] ?? self::LEVEL_FREE)
        ) {
            return false;
        }

        $baseUrl = rtrim((string) (defined('LICENSE_CENTER_URL') ? LICENSE_CENTER_URL : 'http://yutou.wtbidc.cn/license-center'), '/');
        $apiUrl = $baseUrl . '/api.php?action=report_install';
        $payload = [
            'site_id' => $siteId,
            'license_key' => (string) ($license['license_code'] ?? ''),
            'domain' => $domain,
            'license_level' => intval($license['license_level'] ?? self::LEVEL_FREE),
            'install_type' => intval($license['license_level'] ?? self::LEVEL_FREE) > self::LEVEL_FREE ? 'paid' : 'free',
            'version' => self::VERSION,
            'product_name' => '芋头CMS',
            'source' => $source,
        ];

        $ch = curl_init($apiUrl);
        curl_setopt_array($ch, [
            CURLOPT_POST => true,
            CURLOPT_POSTFIELDS => json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES),
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_CONNECTTIMEOUT => 3,
            CURLOPT_TIMEOUT => 5,
            CURLOPT_SSL_VERIFYPEER => false,
            CURLOPT_HTTPHEADER => ['Content-Type: application/json'],
        ]);
        $response = @curl_exec($ch);
        $httpCode = intval(curl_getinfo($ch, CURLINFO_HTTP_CODE));
        curl_close($ch);

        if ($response === false || $httpCode !== 200) {
            return false;
        }

        $result = json_decode($response, true);
        if (!is_array($result) || intval($result['code'] ?? 0) !== 200) {
            return false;
        }

        $cache[$cacheKey] = [
            'time' => time(),
            'domain' => $domain,
            'level' => intval($license['license_level'] ?? self::LEVEL_FREE),
        ];
        $this->saveInstallReportCache($cache);
        return true;
    }

    private function loadInstallReportCache()
    {
        $cacheFile = $this->getInstallReportCacheFile();
        if (!is_file($cacheFile)) {
            return [];
        }

        $content = @file_get_contents($cacheFile);
        if ($content === false || $content === '') {
            return [];
        }

        $data = json_decode($content, true);
        return is_array($data) ? $data : [];
    }

    private function saveInstallReportCache(array $cache)
    {
        $cacheFile = $this->getInstallReportCacheFile();
        $cacheDir = dirname($cacheFile);
        if (!is_dir($cacheDir)) {
            @mkdir($cacheDir, 0755, true);
        }
        @file_put_contents($cacheFile, json_encode($cache, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));
    }

    private function getInstallReportCacheFile()
    {
        return dirname(dirname(__DIR__)) . '/data/install_report_cache.json';
    }

    private function getCurrentHost()
    {
        return trim((string) ($_SERVER['HTTP_HOST'] ?? ''));
    }

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