<?php

namespace Kylin\Licensing\Services;

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

/**
 * 许可证服务
 *
 * 管理软件许可证的激活、停用和状态查询。
 * 通过远程许可证服务器（license.wangk.cn）进行在线验证。
 *
 * 核心功能：
 *  - activate：向远程服务器激活许可证，绑定域名/IP
 *  - deactivate：停用许可证，释放绑定
 *  - getStatusSummary：获取许可证状态摘要（有效期、剩余天数、宽限期等）
 */
class LicenseService
{
    /**
     * @param  LicenseVerifier  $verifier  许可证校验器
     */
    public function __construct(
        private readonly LicenseVerifier $verifier,
    ) {}

    /**
     * 激活许可证
     *
     * 向远程许可证服务器发起激活请求，绑定域名和 IP。
     * 激活成功后更新本地配置并清除缓存，强制下次请求重新校验。
     *
     * @param  string       $licenseKey  许可证密钥
     * @param  string|null  $domain      绑定域名（null 则使用当前请求域名）
     * @param  string|null  $ip          绑定 IP（null 则使用当前请求 IP）
     * @return array                     激活返回数据
     *
     * @throws LicenseExpiredException   许可证无效时抛出
     */
    public function activate(string $licenseKey, ?string $domain = null, ?string $ip = null): array
    {
        $serverUrl = config('kylin-licensing.server_url', 'https://license.wangk.cn');

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

        if (! $response->successful()) {
            $this->audit('license_activation_failed', [
                'license_key' => substr($licenseKey, 0, 8) . '****',
                'domain' => $domain ?? request()->getHost(),
            ]);
            throw new LicenseExpiredException(LicensingErrorCode::LicenseInvalid);
        }

        $data = $response->json('data');

        // 更新环境变量中的许可证密钥
        $this->updateLicenseKeyConfig($licenseKey);

        // 清除缓存，强制下次重新校验
        $this->verifier->clearCache();

        $this->audit('license_activated', [
            'license_key' => substr($licenseKey, 0, 8) . '****',
            'domain' => $domain ?? request()->getHost(),
            'type' => $data['type'] ?? 'standard',
        ]);

        return $data ?? [];
    }

    /**
     * 停用许可证
     *
     * 向远程服务器发送停用请求，释放域名和 IP 绑定。
     * 远程停用失败仅记录警告日志，不阻塞本地操作。
     *
     * @return bool  是否停用成功
     */
    public function deactivate(): bool
    {
        $licenseKey = config('kylin-licensing.key');
        if (empty($licenseKey)) {
            return false;
        }

        $serverUrl = config('kylin-licensing.server_url', 'https://license.wangk.cn');

        try {
            Http::post("{$serverUrl}/api/v1/deactivate", [
                'license_key' => $licenseKey,
                'domain' => request()->getHost(),
                'ip' => request()->ip(),
            ]);
        } catch (\Throwable $e) {
            Log::warning('许可证远程停用失败', [
                'license_key' => $licenseKey,
                'error' => $e->getMessage(),
            ]);
        }

        $this->verifier->clearCache();

        $this->audit('license_deactivated', [
            'license_key' => substr($licenseKey, 0, 8) . '****',
        ]);

        return true;
    }

    /**
     * 获取许可证状态摘要
     *
     * 返回许可证的完整状态信息，用于前端展示和业务判断。
     * 包含：有效期、剩余天数、宽限期、许可证类型、客户信息等。
     *
     * @return array  许可证状态信息数组
     */
    public function getStatusSummary(): array
    {
        $data = $this->verifier->getLicenseData();
        if ($data === null) {
            return ['status' => 'unlicensed', 'message' => '未安装许可证', 'is_valid' => false];
        }

        $status = $data['status'] ?? 0;
        $expiresAt = $data['expires_at'] ?? null;
        $isValid = $status === 1 && ($expiresAt === null || ! \Carbon\Carbon::parse($expiresAt)->isPast());

        $remainingDays = PHP_INT_MAX;
        if ($expiresAt !== null) {
            $remainingDays = (int) now()->diffInDays(\Carbon\Carbon::parse($expiresAt), false);
        }

        return [
            'status' => $status,
            'status_label' => $this->statusLabel($status),
            'is_valid' => $isValid,
            'is_in_grace_period' => $this->isInGracePeriod($data),
            'license_key' => $data['license_key'] ?? '',
            'type' => $data['type'] ?? 'standard',
            'type_label' => $this->typeLabel($data['type'] ?? 'standard'),
            'customer_name' => $data['customer_name'] ?? null,
            'packages' => $data['packages'] ?? null,
            'domain' => $data['domain'] ?? null,
            'max_devices' => $data['max_devices'] ?? 1,
            'issued_at' => $data['issued_at'] ?? null,
            'expires_at' => $expiresAt,
            'remaining_days' => $remainingDays,
        ];
    }

    private function updateLicenseKeyConfig(string $licenseKey): void
    {
        config(['kylin-licensing.key' => $licenseKey]);
    }

    private function statusLabel(int $status): string
    {
        return match ($status) {
            0 => '已禁用',
            1 => '有效',
            2 => '已过期',
            3 => '已暂停',
            4 => '已吊销',
            default => '未知',
        };
    }

    private function typeLabel(string $type): string
    {
        return match ($type) {
            'trial' => '试用版',
            'standard' => '标准版',
            'professional' => '专业版',
            'enterprise' => '企业版',
            default => '未知',
        };
    }

    private function isInGracePeriod(array $data): bool
    {
        $expiresAt = $data['expires_at'] ?? null;
        if ($expiresAt === null) {
            return false;
        }
        $expires = \Carbon\Carbon::parse($expiresAt);
        if (! $expires->isPast()) {
            return false;
        }
        $graceHours = config('kylin-licensing.grace_period', 72);
        return $expires->copy()->addHours($graceHours)->isFuture();
    }

    /**
     * 记录许可证审计日志
     *
     * 依据《计算机软件保护条例》和《著作权法》，
     * 许可证激活、停用等操作需记录审计日志，便于合规审计。
     */
    private function audit(string $event, array $extra = []): void
    {
        if (! class_exists(\Kylin\AuditLog\Models\AuditLog::class)) {
            return;
        }

        try {
            \Kylin\AuditLog\Models\AuditLog::create([
                'event' => $event,
                'model_type' => 'license',
                'model_id' => 0,
                'new_values' => $extra,
                'old_values' => null,
                'user_id' => auth()->id(),
                'ip' => request()?->ip(),
                'url' => request()?->fullUrl(),
                'tag' => 'licensing',
            ]);
        } catch (\Throwable $e) {
            Log::warning('许可证审计日志写入失败', [
                'error' => $e->getMessage(),
                'event' => $event,
            ]);
        }
    }
}
