<?php

namespace Kylin\Licensing\Services;

use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;
use Kylin\Licensing\Enums\LicensingErrorCode;
use Kylin\Licensing\Exceptions\LicenseExpiredException;

/**
 * 许可证验证器
 */
class LicenseVerifier
{
    private ?array $cachedLicenseData = null;
    private ?array $lastError = null;

    public function isValid(): bool
    {
        try {
            $this->assertValid();
            return true;
        } catch (LicenseExpiredException) {
            return false;
        }
    }

    public function assertValid(): void
    {
        $data = $this->resolveLicense();

        if ($data === null) {
            $this->lastError = [
                'code' => LicensingErrorCode::LicenseNotFound->value(),
                'message' => LicensingErrorCode::LicenseNotFound->message(),
            ];
            throw new LicenseExpiredException(LicensingErrorCode::LicenseNotFound);
        }

        $status = $data['status'] ?? 0;

        if ($status === 3) {
            $this->setError(LicensingErrorCode::LicenseSuspended);
            throw new LicenseExpiredException(LicensingErrorCode::LicenseSuspended);
        }

        if ($status === 4) {
            $this->setError(LicensingErrorCode::LicenseRevoked);
            throw new LicenseExpiredException(LicensingErrorCode::LicenseRevoked);
        }

        $expiresAt = $data['expires_at'] ?? null;
        if ($expiresAt !== null) {
            $expires = \Carbon\Carbon::parse($expiresAt);
            if ($expires->isPast()) {
                $graceHours = config('kylin-licensing.grace_period', 72);
                if ($expires->copy()->addHours($graceHours)->isFuture()) {
                    Log::warning('许可证已过期，但在宽限期内', [
                        'license_key' => $data['license_key'] ?? '',
                    ]);
                    $this->setError(LicensingErrorCode::LicenseGracePeriod);
                    return;
                }
                $this->setError(LicensingErrorCode::LicenseExpired);
                throw (new LicenseExpiredException(LicensingErrorCode::LicenseExpired))
                    ->setInGracePeriod(false);
            }
        }

        $this->lastError = null;
    }

    public function assertPackageAuthorized(string $package): void
    {
        $this->assertValid();
        $data = $this->resolveLicense();
        if ($data !== null && ! $this->isPackageAuthorized($data, $package)) {
            $this->setError(LicensingErrorCode::LicensePackageUnauthorized);
            throw new LicenseExpiredException(LicensingErrorCode::LicensePackageUnauthorized);
        }
    }

    public function getLicenseData(): ?array
    {
        return $this->resolveLicense();
    }

    public function getError(): ?array
    {
        return $this->lastError;
    }

    public function clearCache(): void
    {
        Cache::forget('kylin:license:current');
        $this->cachedLicenseData = null;
    }

    private function resolveLicense(): ?array
    {
        if ($this->cachedLicenseData !== null) {
            return $this->cachedLicenseData;
        }

        $licenseKey = config('kylin-licensing.key');
        if (empty($licenseKey)) {
            return null;
        }

        $cacheTtl = config('kylin-licensing.cache_ttl', 3600);
        $cached = Cache::get('kylin:license:current');
        if ($cached !== null) {
            $this->cachedLicenseData = $cached;
            return $cached;
        }

        return $this->verifyWithServer($licenseKey, $cacheTtl);
    }

    private function verifyWithServer(string $licenseKey, int $cacheTtl): ?array
    {
        $serverUrl = config('kylin-licensing.server_url', 'https://license.wangk.cn');

        try {
            $response = Http::timeout(5)->post("{$serverUrl}/api/v1/verify", [
                'license_key' => $licenseKey,
                'domain' => request()->getHost(),
                'ip' => request()->ip(),
            ]);

            if (! $response->successful()) {
                Log::warning('许可证远程校验失败', [
                    'license_key' => $licenseKey,
                    'status' => $response->status(),
                ]);
                return null;
            }

            $data = $response->json('data');
            if (empty($data)) {
                return null;
            }

            // API 返回 200 且有 data 即视为验证通过，补充默认值
            $data['status'] = $data['status'] ?? 1;
            $data['license_key'] = $data['license_key'] ?? $licenseKey;

            // 缓存远程校验结果
            Cache::put('kylin:license:current', $data, $cacheTtl);
            $this->cachedLicenseData = $data;
            return $data;
        } catch (\Throwable $e) {
            Log::error('许可证远程校验异常', [
                'license_key' => $licenseKey,
                'error' => $e->getMessage(),
            ]);
            return null;
        }
    }

    private function isPackageAuthorized(array $data, string $package): bool
    {
        $packages = $data['packages'] ?? null;
        if (empty($packages)) {
            return true;
        }
        return in_array($package, $packages, true)
            || in_array('*', $packages, true);
    }

    private function setError(LicensingErrorCode $code): void
    {
        $this->lastError = ['code' => $code->value(), 'message' => $code->message()];
    }
}
