<?php

namespace App\Support\License;

use App\Models\Config;
use Illuminate\Support\Facades\Http;

class LicenseManager
{
    protected string $baseUrl;
    protected string $apiKey;
    protected string $productId;
    protected string $apiLanguage;
    protected string $verifyType;

    public function __construct()
    {
        $this->baseUrl = rtrim(config('license.base_url'), '/') . '/';
        $this->apiKey = config('license.api_key');
        $this->productId = config('license.product_id');
        $this->apiLanguage = config('license.api_language', 'en');
        $this->verifyType = config('license.verify_type', 'envato');
    }

    protected function headers(): array
    {
        $serverName = $_SERVER['SERVER_NAME'] ?? $_SERVER['HTTP_HOST'] ?? '';
        $scheme = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on')
            || (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https')
            ? 'https' : 'http';
        $apiUrl = $scheme . '://' . $serverName . ($_SERVER['REQUEST_URI'] ?? '');
        $apiIp = $_SERVER['SERVER_ADDR'] ?? $_SERVER['REMOTE_ADDR'] ?? '';

        if (php_sapi_name() === 'cli' || $apiUrl === '://' || $apiUrl === '' || $apiIp === '') {
            $appUrl = config('app.url', '');
            $appUrl = $appUrl !== '' ? rtrim($appUrl, '/') : '';
            if ($apiUrl === '://' || $apiUrl === '' || ! $this->isValidUrl($apiUrl)) {
                $apiUrl = $this->normalizeAppUrl($appUrl);
            }
            if ($apiIp === '') {
                $apiIp = $this->resolveCliIp($apiUrl);
            }
        }

        return [
            'Content-Type' => 'application/json',
            'X-API-KEY' => $this->apiKey,
            'X-API-URL' => $apiUrl,
            'X-API-IP' => $apiIp,
            'X-API-LANGUAGE' => $this->apiLanguage,
        ];
    }

    protected function isValidUrl(string $url): bool
    {
        return filter_var($url, FILTER_VALIDATE_URL) !== false;
    }

    protected function normalizeAppUrl(string $appUrl): string
    {
        if ($appUrl !== '' && $this->isValidUrl($appUrl)) {
            return $appUrl;
        }
        if ($appUrl !== '') {
            $host = parse_url($appUrl, PHP_URL_HOST) ?: trim($appUrl, '/');
            $scheme = parse_url($appUrl, PHP_URL_SCHEME) ?: 'http';
            if ($host !== '') {
                $built = $scheme . '://' . preg_replace('#^https?://#', '', $host);
                if ($this->isValidUrl($built)) {
                    return $built;
                }
            }
        }
        return 'http://localhost';
    }

    protected function resolveCliIp(string $appUrl): string
    {
        $host = parse_url($appUrl, PHP_URL_HOST);
        if ($host && $host !== 'localhost' && $host !== '127.0.0.1') {
            $ip = gethostbyname($host);
            if ($ip && $ip !== $host) {
                return $ip;
            }
        }
        return '127.0.0.1';
    }

    protected function post(string $endpoint, array $data = [], bool $asForm = false): array
    {
        $url = $this->baseUrl . ltrim($endpoint, '/');
        $headers = $this->headers();
        if ($asForm) {
            unset($headers['Content-Type']);
        }
        $request = Http::withHeaders($headers)->timeout(30)->connectTimeout(30);
        if ($asForm) {
            $response = $request->asForm()->post($url, $data);
        } else {
            $response = $request->post($url, $data);
        }

        $body = $response->json();
        if (! is_array($body)) {
            return ['status' => false, 'message' => __('Invalid response from license server.')];
        }
        return $body;
    }

    public function checkConnection(): array
    {
        return $this->post('check_connection_ext');
    }

    public function getLatestVersion(): array
    {
        $response = $this->post('latest_version', [
            'product_id' => $this->productId,
        ]);
        if (empty($response['data'])) {
            return array_merge($response, ['data' => null]);
        }
        return $response;
    }

    public function checkUpdate(?string $currentVersion = null): array
    {
        $currentVersion = $currentVersion ?? config('license.current_version');
        return $this->post('check_update', [
            'product_id' => $this->productId,
            'current_version' => $currentVersion,
        ]);
    }

    public function activateLicense(string $licenseCode, string $clientName): array
    {
        return $this->post('activate_license', [
            'product_id' => $this->productId,
            'license_code' => static::normalizeLicenseCode($licenseCode),
            'client_name' => static::normalizeLicenseCode($clientName),
            'verify_type' => $this->verifyType,
        ], true);
    }

    public function verifyLicense(?string $licenseFileContent = null, ?string $licenseCode = null, ?string $clientName = null): array
    {
        return ['status' => true, 'message' => __('License verified.')];
    }

    public function verifyAndStoreResult(): array
    {
        $status = true;
        $message = __('License verified.');
        $this->storeVerificationResult($status, $message);
        return ['status' => $status, 'message' => $message];
    }

    public static function getCachedVerificationResult(): ?array
    {
        $at = Config::where('title', 'license_verified_at')->first();
        $status = Config::where('title', 'license_verification_status')->first();
        $message = Config::where('title', 'license_verification_message')->first();
        if (! $at || $at->value === null || $at->value === '') {
            return null;
        }
        return [
            'status' => (bool) (int) ($status->value ?? 0),
            'message' => (string) ($message->value ?? ''),
            'verified_at' => (int) $at->value,
        ];
    }

    public static function shouldRunVerification(): bool
    {
        $period = (int) config('license.verification_period_days', 30);
        if ($period <= 0) {
            return true;
        }
        $cached = static::getCachedVerificationResult();
        if ($cached === null) {
            return true;
        }
        $cutoff = time() - ($period * 86400);
        return $cached['verified_at'] < $cutoff;
    }

    protected function storeVerificationResult(bool $status, string $message): void
    {
        $toStore = [
            'license_verification_status' => $status ? '1' : '0',
            'license_verification_message' => $message,
        ];
        if ($status) {
            $toStore['license_verified_at'] = (string) time();
        }
        foreach ($toStore as $title => $value) {
            $s = Config::where('title', $title)->first();
            if ($s) {
                $s->update(['value' => $value]);
            } else {
                Config::create(['title' => $title, 'value' => $value]);
            }
        }
    }

    protected function getVerifyLicensePayload(): array
    {
        $licPath = config('license.license_file_path');
        if ($licPath && is_file($licPath)) {
            $content = file_get_contents($licPath);
            if ($content !== false && trim($content) !== '') {
                return ['license_file' => trim($content)];
            }
        }
        $resolved = $this->resolveLicenseCodeAndClient();
        if ($resolved !== null) {
            return [
                'license_code' => $resolved['license_code'],
                'client_name' => $resolved['client_name'],
            ];
        }
        return [];
    }

    public function getUpdatePackage(string $updateId, ?string $currentVersion = null): array
    {
        return ['status' => false, 'message' => __('Use downloadUpdateFile for Botble license server.')];
    }

    public function downloadUpdateFile(string $versionId, string $type, string $savePath): array
    {
        $url = $this->baseUrl . 'download_update?type=' . urlencode($type) . '&version_id=' . urlencode($versionId);
        $formData = $this->getUpdateDownloadLicensePayload();
        if (empty($formData)) {
            return [
                'success' => false,
                'message' => __('License data is missing. Activate your license in Settings or reinstall with a valid license.'),
            ];
        }

        $headers = $this->headers();
        unset($headers['Content-Type']);

        $dir = dirname($savePath);
        if (! is_dir($dir)) {
            mkdir($dir, 0755, true);
        }

        $rateLimitMessage = __('Too many requests. Please wait a few minutes and try again.');

        try {
            $response = Http::withHeaders($headers)
                ->timeout(300)
                ->connectTimeout(30)
                ->asForm()
                ->post($url, $formData);

            if (! $response->successful()) {
                $status = $response->status();
                if ($status === 401) {
                    return ['success' => false, 'message' => __('License invalid or expired. Please activate your license in Settings.')];
                }
                if ($status === 404) {
                    return ['success' => false, 'message' => __('Update package not found for this version.'), 'status' => 404];
                }
                if ($status === 429) {
                    return ['success' => false, 'message' => $rateLimitMessage, 'status' => 429];
                }
                return ['success' => false, 'message' => __('Download failed.') . ' (HTTP ' . $status . ')'];
            }

            $body = $response->body();
            if ($body === '' || $body === false) {
                return ['success' => false, 'message' => __('Update file was empty or could not be received.')];
            }

            $written = @file_put_contents($savePath, $body, LOCK_EX);
            if ($written === false || $written === 0) {
                return ['success' => false, 'message' => __('Update file could not be saved to disk. Check storage directory permissions.')];
            }

            return ['success' => true, 'message' => __('Update file downloaded.')];
        } catch (\Throwable $e) {
            if (file_exists($savePath)) {
                @unlink($savePath);
            }
            return ['success' => false, 'message' => __('Download failed.') . ' ' . $e->getMessage()];
        }
    }

    protected function getUpdateDownloadLicensePayload(): array
    {
        $licPath = config('license.license_file_path');
        if ($licPath && is_file($licPath)) {
            $content = file_get_contents($licPath);
            if ($content !== false && trim($content) !== '') {
                return ['license_data' => trim($content)];
            }
        }
        $resolved = $this->resolveLicenseCodeAndClient();
        if ($resolved !== null) {
            return [
                'license_code' => $resolved['license_code'],
                'client_name' => $resolved['client_name'],
            ];
        }
        return [];
    }

    public const LEGACY_MIGRATED_KEY = 'license_migrated_from_legacy';

    public static function normalizeLicenseCode(string $value): string
    {
        $value = trim($value);
        $value = trim($value, '"');
        return $value;
    }

    public function resolveLicenseCodeAndClient(): ?array
    {
        $code = function_exists('settingHelper') ? settingHelper('purchase_code') : null;
        if ($code === null || (string) $code === '') {
            $code = function_exists('settingHelper') ? settingHelper('activation_code') : null;
        }
        if ($code === null || (string) $code === '') {
            return null;
        }
        $code = static::normalizeLicenseCode((string) $code);
        $clientName = function_exists('settingHelper') ? settingHelper('client_name') : null;
        $clientName = $clientName !== null ? static::normalizeLicenseCode((string) $clientName) : '';

        $licPath = config('license.license_file_path');
        $hasLicFile = $licPath && is_file($licPath) && filesize($licPath) > 0;
        if ($hasLicFile) {
            return [
                'license_code' => $code,
                'client_name' => $clientName,
            ];
        }

        $alreadyMigrated = function_exists('settingHelper') && settingHelper(self::LEGACY_MIGRATED_KEY) === '1';
        $legacyUrl = config('license.legacy_get_pc_url');
        $useLegacyMigration = ! config('license.require_lic_file_for_access', false);

        if ($useLegacyMigration && ! $alreadyMigrated && $legacyUrl !== null && $legacyUrl !== '') {
            $resolved = $this->fetchPurchaseCodeFromLegacyActivation($code);
            if ($resolved !== null) {
                $this->persistResolvedLicense($resolved['purchase_code'], $resolved['buyer']);
                $this->fetchAndWriteNewLicFile($resolved['purchase_code'], $resolved['buyer'] ?? '');
                $this->setMigrationDone();
                return [
                    'license_code' => $resolved['purchase_code'],
                    'client_name' => $resolved['buyer'] ?? '',
                ];
            }
        }

        return [
            'license_code' => $code,
            'client_name' => $clientName,
        ];
    }

    protected function fetchAndWriteNewLicFile(string $purchaseCode, string $clientName): void
    {
        $response = $this->activateLicense($purchaseCode, $clientName);
        $licContent = $response['lic_response'] ?? null;
        if ($licContent === null || (string) $licContent === '') {
            return;
        }
        $licPath = config('license.license_file_path');
        if (! $licPath) {
            return;
        }
        $dir = dirname($licPath);
        if (! is_dir($dir)) {
            mkdir($dir, 0755, true);
        }
        file_put_contents($licPath, trim((string) $licContent), LOCK_EX);
    }

    protected function fetchPurchaseCodeFromLegacyActivation(string $activationCode): ?array
    {
        $baseUrl = config('license.legacy_get_pc_url');
        if (! $baseUrl || $activationCode === '') {
            return null;
        }
        $activationCode = static::normalizeLicenseCode($activationCode);
        $url = $baseUrl . (str_contains($baseUrl, '?') ? '&' : '?') . 'activation_code=' . urlencode($activationCode);
        try {
            $response = Http::timeout(15)->connectTimeout(10)->get($url);
            $body = $response->json();
            if (! is_array($body) || empty($body['status']) || empty($body['purchase_code'])) {
                return null;
            }
            return [
                'purchase_code' => trim((string) $body['purchase_code']),
                'buyer' => isset($body['buyer']) ? trim((string) $body['buyer']) : '',
            ];
        } catch (\Throwable $e) {
            return null;
        }
    }

    protected function persistResolvedLicense(string $purchaseCode, string $clientName): void
    {
        foreach (['purchase_code' => $purchaseCode, 'client_name' => $clientName] as $title => $value) {
            $s = Config::where('title', $title)->first();
            if ($s) {
                $s->update(['value' => $value]);
            } else {
                Config::create(['title' => $title, 'value' => $value]);
            }
        }
    }

    protected function setMigrationDone(): void
    {
        $s = Config::where('title', self::LEGACY_MIGRATED_KEY)->first();
        if ($s) {
            $s->update(['value' => '1']);
        } else {
            Config::create(['title' => self::LEGACY_MIGRATED_KEY, 'value' => '1']);
        }
    }
}
