<?php

declare(strict_types=1);

namespace App\Services\License;

use App\Core\Env;

final class LicenseManager
{
    public function __construct(private array $config = [])
    {
    }

    public function evaluate(): array
    {
        $fingerprint = ServerFingerprint::value($this->config);
        $licensePath = APP_BASE_PATH . '/storage/license/license.json';
        $publicKeyPath = APP_BASE_PATH . '/storage/license/public.key';
        $devOverridePath = APP_BASE_PATH . '/storage/license/dev-override.json';

        $state = [
            'valid' => false,
            'read_only' => true,
            'code' => 'LICENSE_MISSING',
            'message' => 'الترخيص غير موجود. النظام يعمل بوضع القراءة فقط حتى تفعيل الترخيص.',
            'path' => $licensePath,
            'public_key_path' => $publicKeyPath,
            'dev_override_path' => $devOverridePath,
            'fingerprint' => $fingerprint,
            'license' => null,
            'dev_override' => null,
            'dev_requested' => $this->isDevEnvironmentRequested(),
            'policy' => 'offline',
        ];

        $publicKey = $this->loadPublicKey($publicKeyPath);
        if ($publicKey === null) {
            $state['code'] = 'LICENSE_PUBLIC_KEY_MISSING';
            $state['message'] = 'ملف المفتاح العام للترخيص غير موجود أو غير صالح. النظام يعمل بوضع القراءة فقط.';
            return $this->evaluateDevOverride($state, $publicKey);
        }

        $license = $this->loadSignedJson($licensePath);
        if ($license === null) {
            return $this->evaluateDevOverride($state, $publicKey);
        }

        $state['license'] = $license;

        if (strtolower((string) ($license['type'] ?? 'offline_license')) !== 'offline_license') {
            $state['code'] = 'LICENSE_INVALID_TYPE';
            $state['message'] = 'نوع ملف الترخيص غير صحيح.';
            return $this->evaluateDevOverride($state, $publicKey);
        }

        if (!$this->verifySignedDocument($license, $publicKey)) {
            $state['code'] = 'LICENSE_INVALID_SIGNATURE';
            $state['message'] = 'توقيع الترخيص غير صالح أو تم العبث بملف الترخيص. النظام يعمل بوضع القراءة فقط.';
            return $this->evaluateDevOverride($state, $publicKey);
        }

        $expected = (string) ($license['fingerprint'] ?? '');
        if ($expected === '' || !hash_equals($expected, $fingerprint)) {
            $state['code'] = 'LICENSE_MACHINE_MISMATCH';
            $state['message'] = 'الترخيص غير مطابق لهذا الجهاز/السيرفر. النظام يعمل بوضع القراءة فقط.';
            return $this->evaluateDevOverride($state, $publicKey);
        }

        $status = strtolower(trim((string) ($license['status'] ?? 'active')));
        if (in_array($status, ['revoked', 'canceled', 'disabled'], true)) {
            $state['code'] = 'LICENSE_REVOKED';
            $state['message'] = 'هذا الترخيص مُلغى منطقيًا. النظام يعمل بوضع القراءة فقط.';
            return $this->evaluateDevOverride($state, $publicKey);
        }
        if ($status !== '' && !in_array($status, ['active', 'trial'], true)) {
            $state['code'] = 'LICENSE_INACTIVE';
            $state['message'] = 'حالة الترخيص غير مفعلة. النظام يعمل بوضع القراءة فقط.';
            return $this->evaluateDevOverride($state, $publicKey);
        }

        $issuedAt = trim((string) ($license['issued_at'] ?? ''));
        if ($issuedAt !== '' && strtotime($issuedAt) !== false && strtotime($issuedAt) > time() + 300) {
            $state['code'] = 'LICENSE_NOT_YET_VALID';
            $state['message'] = 'تاريخ إصدار الترخيص أكبر من الوقت الحالي بشكل غير منطقي.';
            return $this->evaluateDevOverride($state, $publicKey);
        }

        $expiresAt = trim((string) ($license['expires_at'] ?? ''));
        if ($expiresAt !== '' && strtotime($expiresAt) !== false && strtotime($expiresAt) < time()) {
            $state['code'] = 'LICENSE_EXPIRED';
            $state['message'] = 'انتهت صلاحية الترخيص. النظام يعمل بوضع القراءة فقط.';
            return $this->evaluateDevOverride($state, $publicKey);
        }

        $state['valid'] = true;
        $state['read_only'] = false;
        $state['code'] = 'LICENSE_OK';
        $state['message'] = 'الترخيص صالح ويعمل Offline بدون اتصال بخادم خارجي.';
        return $state;
    }

    public function activationTemplate(): array
    {
        return [
            'type' => 'offline_license',
            'license_id' => 'LIC-2026-000001',
            'customer' => 'Customer Name',
            'product' => 'Store ERP',
            'issued_at' => date(DATE_ATOM),
            'expires_at' => date(DATE_ATOM, strtotime('+1 year')),
            'fingerprint' => ServerFingerprint::value($this->config),
            'status' => 'active',
            'features' => [
                'sales' => true,
                'inventory' => true,
                'reports' => true,
                'pos' => true,
            ],
            'revision' => 1,
            'reason' => '',
            'notes' => '',
            'signature' => 'PUT_BASE64_SIGNATURE_HERE',
        ];
    }

    public function activationRequestTemplate(): array
    {
        return [
            'type' => 'offline_activation_request',
            'product' => 'Store ERP',
            'generated_at' => date(DATE_ATOM),
            'fingerprint' => ServerFingerprint::value($this->config),
            'fingerprint_data' => ServerFingerprint::collect($this->config),
            'license_target_path' => APP_BASE_PATH . '/storage/license/license.json',
        ];
    }

    public function devOverrideTemplate(): array
    {
        return [
            'type' => 'dev_override',
            'issued_at' => date(DATE_ATOM),
            'expires_at' => date(DATE_ATOM, strtotime('+14 days')),
            'fingerprint' => ServerFingerprint::value($this->config),
            'allow_write' => true,
            'note' => 'Temporary signed development override',
            'signature' => 'PUT_BASE64_SIGNATURE_HERE',
        ];
    }

    public function signingPayloadTemplate(): array
    {
        return [
            'normal_license' => $this->canonicalPayload($this->activationTemplate()),
            'activation_request' => $this->canonicalize($this->activationRequestTemplate()),
            'dev_override' => $this->canonicalPayload($this->devOverrideTemplate()),
        ];
    }

    public function canonicalPayload(array $document): string
    {
        unset($document['signature']);
        return $this->canonicalize($document);
    }

    public function isDeveloperConsoleEnabled(): bool
    {
        $privateKeyPath = APP_BASE_PATH . '/storage/license/private.key';
        if (!is_file($privateKeyPath)) {
            return false;
        }
        $raw = trim((string) @file_get_contents($privateKeyPath));
        return $raw !== '' && !str_contains($raw, 'PUT_BASE64_');
    }

    private function evaluateDevOverride(array $state, ?string $publicKey): array
    {
        if (!$state['dev_requested']) {
            return $state;
        }

        if ($publicKey === null) {
            $state['message'] .= ' كما أن bypass التطوير المعتمد يحتاج مفتاحًا عامًا صحيحًا.';
            return $state;
        }

        $override = $this->loadSignedJson((string) $state['dev_override_path']);
        if ($override === null) {
            $state['code'] = 'DEV_OVERRIDE_REQUIRED';
            $state['message'] = 'تم طلب وضع تطوير، لكن لا يوجد dev-override موقّع. تعديل APP_ENV أو APP_DEBUG وحده لا يتجاوز الحماية.';
            return $state;
        }

        $state['dev_override'] = $override;

        if (!$this->verifySignedDocument($override, $publicKey)) {
            $state['code'] = 'DEV_OVERRIDE_INVALID_SIGNATURE';
            $state['message'] = 'ملف dev-override غير صالح أو تم العبث به. تعديل APP_ENV أو APP_DEBUG وحده لا يتجاوز الحماية.';
            return $state;
        }

        if (strtolower((string) ($override['type'] ?? '')) !== 'dev_override') {
            $state['code'] = 'DEV_OVERRIDE_INVALID_TYPE';
            $state['message'] = 'ملف dev-override غير صحيح.';
            return $state;
        }

        $expected = (string) ($override['fingerprint'] ?? '');
        if ($expected === '' || !hash_equals($expected, (string) $state['fingerprint'])) {
            $state['code'] = 'DEV_OVERRIDE_MACHINE_MISMATCH';
            $state['message'] = 'ملف dev-override لا يطابق هذا الجهاز/السيرفر.';
            return $state;
        }

        $expiresAt = trim((string) ($override['expires_at'] ?? ''));
        if ($expiresAt !== '' && strtotime($expiresAt) !== false && strtotime($expiresAt) < time()) {
            $state['code'] = 'DEV_OVERRIDE_EXPIRED';
            $state['message'] = 'انتهت صلاحية dev-override. النظام يبقى بوضع القراءة فقط.';
            return $state;
        }

        if (!($override['allow_write'] ?? false)) {
            $state['code'] = 'DEV_OVERRIDE_READ_ONLY';
            $state['message'] = 'ملف dev-override الحالي لا يسمح بالكتابة.';
            return $state;
        }

        $state['valid'] = true;
        $state['read_only'] = false;
        $state['code'] = 'LICENSE_DEV_OVERRIDE_OK';
        $state['message'] = 'تم تفعيل وضع التطوير الموقّع. تعديل APP_ENV أو APP_DEBUG وحده لا يكفي.';
        return $state;
    }

    private function loadSignedJson(string $path): ?array
    {
        if (!is_file($path)) {
            return null;
        }

        $raw = @file_get_contents($path);
        $decoded = json_decode((string) $raw, true);
        return is_array($decoded) ? $decoded : null;
    }

    private function loadPublicKey(string $path): ?string
    {
        if (!is_file($path)) {
            return null;
        }

        $raw = trim((string) @file_get_contents($path));
        if ($raw === '' || str_contains($raw, 'PUT_BASE64_')) {
            return null;
        }

        $key = base64_decode($raw, true);
        if ($key === false || $key === '') {
            return null;
        }

        return $key;
    }

    private function verifySignedDocument(array $document, string $publicKey): bool
    {
        $signatureB64 = (string) ($document['signature'] ?? '');
        if ($signatureB64 === '') {
            return false;
        }

        $signature = base64_decode($signatureB64, true);
        if ($signature === false || $signature === '') {
            return false;
        }

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

        return sodium_crypto_sign_verify_detached($signature, $this->canonicalPayload($document), $publicKey);
    }

    private function canonicalize(mixed $value): string
    {
        if (is_array($value)) {
            $isAssoc = array_keys($value) !== range(0, count($value) - 1);
            if ($isAssoc) {
                ksort($value);
            }

            $normalized = [];
            foreach ($value as $k => $v) {
                $normalized[$k] = json_decode($this->canonicalize($v), true);
            }

            return json_encode($normalized, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
        }

        return json_encode($value, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
    }

    private function isDevEnvironmentRequested(): bool
    {
        $env = strtolower((string) ($this->config['app']['env'] ?? Env::get('APP_ENV', 'production')));
        $debug = (bool) ($this->config['app']['debug'] ?? Env::bool('APP_DEBUG', false));
        return in_array($env, ['local', 'development', 'dev'], true) || $debug;
    }
}
