<?php

namespace GriffinForms\Includes\Packages;

use GriffinForms\Traits\Singleton;

class PackageStateResolver extends \GriffinForms\Format
{
    use Singleton;

    protected $registry;
    protected $approval;
    protected $compatibility;
    protected $entitlement;
    protected $store;

    protected function setRegistry()
    {
        $this->registry = PackageRegistry::getInstance();
    }

    protected function setApproval()
    {
        $this->approval = PackageApproval::getInstance();
    }

    protected function setCompatibility()
    {
        $this->compatibility = PackageCompatibility::getInstance();
    }

    protected function setEntitlement()
    {
        $this->entitlement = PackageEntitlement::getInstance();
    }

    protected function setStore()
    {
        $this->store = PackageStateStore::getInstance();
    }

    public function getState(bool $refresh = false): array
    {
        if (!$refresh) {
            $snapshot = $this->store->getSnapshot();
            if (!empty($snapshot['packages']) && is_array($snapshot['packages'])) {
                $reconciled = $this->reconcileLivePackageState($snapshot);
                if ($reconciled !== $snapshot) {
                    $this->store->saveSnapshot($reconciled);
                }

                return $reconciled;
            }
        }

        $state = $this->resolveState();
        $state = $this->reconcileLivePackageState($state);
        $this->store->saveSnapshot($state);

        return $state;
    }

    public function refreshState(): array
    {
        return $this->getState(true);
    }

    protected function resolveState(): array
    {
        $packages = [];

        foreach ($this->registry->getAll() as $package_id => $package) {
            $package = $this->hydratePluginMetadata($package);
            $is_present = !empty($package['is_present']);
            $is_active = !empty($package['is_active']);
            $approval_status = $this->approval->getApprovalStatus($package);
            $compatibility_state = $this->compatibility->getCompatibilityState($package, $is_present);
            $entitlement = $this->entitlement->resolveState($package, $is_present, $is_active);
            $entitlement_state = (string) ($entitlement['entitlement_state'] ?? 'not_applicable');
            $integrity_state = $this->resolveIntegrityState($package, $is_present);
            $update_state = $this->resolveUpdateState($package, $is_present, $entitlement_state);

            $packages[$package_id] = [
                'package_id' => $package_id,
                'package_type' => (string) ($package['package_type'] ?? 'first_party_extension'),
                'package_label_internal' => (string) ($package['package_label_internal'] ?? ucfirst($package_id)),
                'package_version' => $package['package_version'] ?? null,
                'package_bootstrap_file' => (string) ($package['package_bootstrap_file'] ?? ''),
                'is_present' => $is_present,
                'is_active' => $is_active,
                'licensing_provider' => (string) ($package['licensing_provider'] ?? ''),
                'runtime_access_state' => (string) ($package['runtime_access_state'] ?? ''),
                'premium_runtime_enabled' => !empty($package['runtime_gate']['premium_runtime_enabled']),
                'approval_status' => $approval_status,
                'compatibility_state' => $compatibility_state,
                'entitlement_state' => $entitlement_state,
                'license_key_present' => !empty($entitlement['license_key_present']),
                'verification_state' => (string) ($entitlement['verification_state'] ?? ''),
                'last_verified_at' => (string) ($entitlement['last_verified_at'] ?? ''),
                'last_successful_verification_at' => (string) ($entitlement['last_successful_verification_at'] ?? ''),
                'last_failed_verification_at' => (string) ($entitlement['last_failed_verification_at'] ?? ''),
                'next_retry_at' => (string) ($entitlement['next_retry_at'] ?? ''),
                'grace_started_at' => (string) ($entitlement['grace_started_at'] ?? ''),
                'grace_expires_at' => (string) ($entitlement['grace_expires_at'] ?? ''),
                'license_expires_at' => (string) ($entitlement['license_expires_at'] ?? ''),
                'license_last_action' => (string) ($entitlement['license_last_action'] ?? ''),
                'verification_failure_count' => max(0, absint($entitlement['failure_count'] ?? 0)),
                'integrity_state' => $integrity_state,
                'update_state' => $update_state,
                'recommended_user_action' => $this->resolveRecommendedUserAction($package_id, $is_present, $is_active, $compatibility_state, $entitlement_state),
                'warning_layer_key' => $this->resolveWarningLayerKey($compatibility_state, $entitlement_state),
                'blocking_reason_key' => $this->resolveBlockingReasonKey($approval_status, $compatibility_state, $entitlement_state),
            ];
        }

        return [
            'resolved_at' => gmdate('c'),
            'core_package_id' => 'core',
            'packages' => $packages,
        ];
    }

    protected function reconcileLivePackageState(array $state): array
    {
        $packages = is_array($state['packages'] ?? null) ? $state['packages'] : [];
        if (empty($packages)) {
            return $state;
        }

        foreach ($packages as $package_id => $package_state) {
            if (!is_array($package_state)) {
                continue;
            }

            $packages[$package_id] = $this->reconcileLivePackageStateForPackage((string) $package_id, $package_state);
        }

        $state['packages'] = $packages;

        return $state;
    }

    protected function reconcileLivePackageStateForPackage(string $package_id, array $package_state): array
    {
        $live_entitlement = $this->getLiveEntitlementStateForPackage($package_id);
        if (empty($live_entitlement)) {
            return $package_state;
        }

        $current_entitlement_state = (string) ($package_state['entitlement_state'] ?? '');
        $live_entitlement_state = (string) ($live_entitlement['entitlement_state'] ?? '');
        $current_license_key_present = !empty($package_state['license_key_present']);
        $live_license_key_present = !empty($live_entitlement['license_key_present']);
        $current_license_expires_at = (string) ($package_state['license_expires_at'] ?? '');
        $live_license_expires_at = (string) ($live_entitlement['license_expires_at'] ?? '');

        if (
            $current_entitlement_state === $live_entitlement_state
            && $current_license_key_present === $live_license_key_present
            && $current_license_expires_at === $live_license_expires_at
        ) {
            return $package_state;
        }

        $package = $this->registry->get($package_id);
        if (!is_array($package)) {
            return $package_state;
        }

        $package = $this->hydratePluginMetadata($package);
        $is_present = !empty($package_state['is_present']);
        $is_active = !empty($package_state['is_active']);
        $compatibility_state = (string) ($package_state['compatibility_state'] ?? 'unknown');
        $approval_status = (string) ($package_state['approval_status'] ?? 'approved');
        $entitlement_state = $live_entitlement_state !== '' ? $live_entitlement_state : $current_entitlement_state;

        $package_state['entitlement_state'] = $entitlement_state;
        $package_state['license_key_present'] = $live_license_key_present;
        $package_state['verification_state'] = (string) ($live_entitlement['verification_state'] ?? $package_state['verification_state'] ?? '');
        $package_state['license_expires_at'] = $live_license_expires_at;
        $package_state['update_state'] = $this->resolveUpdateState($package, $is_present, $entitlement_state);
        $package_state['recommended_user_action'] = $this->resolveRecommendedUserAction($package_id, $is_present, $is_active, $compatibility_state, $entitlement_state);
        $package_state['warning_layer_key'] = $this->resolveWarningLayerKey($compatibility_state, $entitlement_state);
        $package_state['blocking_reason_key'] = $this->resolveBlockingReasonKey($approval_status, $compatibility_state, $entitlement_state);

        return $package_state;
    }

    protected function getLiveEntitlementStateForPackage(string $package_id): array
    {
        if ($package_id !== 'pro') {
            return [];
        }

        if (!class_exists('\\GriffinFormsPro\\Includes\\Licensing\\Freemius')) {
            return [];
        }

        try {
            $record = \GriffinFormsPro\Includes\Licensing\Freemius::getInstance()->getEntitlementRecord();
        } catch (\Throwable $throwable) {
            return [];
        }

        if (!is_array($record)) {
            return [];
        }

        $entitlement_state = sanitize_key((string) ($record['state'] ?? ''));
        if (!in_array($entitlement_state, ['key_not_entered', 'verified_active', 'verified_inactive_or_expired'], true)) {
            return [];
        }

        $license_expires_at = trim((string) ($record['license_expires_at'] ?? ''));
        if ($license_expires_at !== '') {
            $timestamp = strtotime($license_expires_at);
            $license_expires_at = $timestamp !== false ? gmdate('c', $timestamp) : '';
        }

        return [
            'entitlement_state' => $entitlement_state,
            'license_key_present' => !empty($record['license_key_present']),
            'verification_state' => $entitlement_state,
            'license_expires_at' => $license_expires_at,
        ];
    }

    protected function hydratePluginMetadata(array $package): array
    {
        $package['is_present'] = false;
        $package['is_active'] = false;

        $bootstrap_file = (string) ($package['package_bootstrap_file'] ?? '');
        if ($bootstrap_file === '') {
            return $package;
        }

        if (!function_exists('get_plugins')) {
            require_once ABSPATH . 'wp-admin/includes/plugin.php';
        }

        $plugins = get_plugins();
        if (isset($plugins[$bootstrap_file])) {
            $package['is_present'] = true;
            $package['package_version'] = (string) ($plugins[$bootstrap_file]['Version'] ?? ($package['package_version'] ?? ''));
            $package['package_label_internal'] = (string) ($plugins[$bootstrap_file]['Name'] ?? ($package['package_label_internal'] ?? ''));
        }

        if (function_exists('is_plugin_active')) {
            $package['is_active'] = is_plugin_active($bootstrap_file);
        }

        return $package;
    }

    protected function resolveIntegrityState(array $package, bool $is_present): string
    {
        $package_id = sanitize_key((string) ($package['package_id'] ?? ''));

        if ($package_id === 'core') {
            return 'trusted_self';
        }

        if (!$is_present) {
            return 'not_checked';
        }

        return 'approved_unchecked';
    }

    protected function resolveUpdateState(array $package, bool $is_present, string $entitlement_state): string
    {
        if (empty($package['supports_updates'])) {
            return $is_present ? 'not_applicable' : 'not_installed';
        }

        if (!$is_present) {
            return 'not_installed';
        }

        if (!in_array($entitlement_state, ['verified_active', 'grace_period', 'not_applicable'], true)) {
            return 'gated_by_entitlement';
        }

        $bootstrap_file = (string) ($package['package_bootstrap_file'] ?? '');
        if ($bootstrap_file !== '') {
            $updates = get_site_transient('update_plugins');
            $response = is_object($updates) && is_array($updates->response ?? null) ? $updates->response : [];
            if (!empty($response[$bootstrap_file])) {
                return 'update_available';
            }
        }

        return 'up_to_date';
    }

    protected function resolveRecommendedUserAction(string $package_id, bool $is_present, bool $is_active, string $compatibility_state, string $entitlement_state): ?string
    {
        if ($package_id === 'core') {
            return null;
        }

        if (!$is_present) {
            return 'install_' . $package_id;
        }

        if (!$is_active) {
            return 'activate_' . $package_id;
        }

        if ($compatibility_state !== 'compatible') {
            return 'resolve_compatibility';
        }

        if ($entitlement_state === 'key_not_entered') {
            return 'enter_license_key';
        }

        if ($entitlement_state === 'verified_inactive_or_expired') {
            return 'reactivate_license';
        }

        if ($entitlement_state === 'unverified_connection_issue') {
            return 'retry_license_verification';
        }

        if ($entitlement_state === 'grace_period') {
            return 'monitor_grace_period';
        }

        return null;
    }

    protected function resolveWarningLayerKey(string $compatibility_state, string $entitlement_state): ?string
    {
        if ($compatibility_state !== 'compatible' && $compatibility_state !== 'unknown') {
            return 'package_compatibility';
        }

        if ($entitlement_state === 'key_not_entered') {
            return 'license_key_missing';
        }

        if ($entitlement_state === 'verified_inactive_or_expired') {
            return 'license_inactive';
        }

        if ($entitlement_state === 'unverified_connection_issue') {
            return 'license_connection_issue';
        }

        if ($entitlement_state === 'grace_period') {
            return 'license_grace_period';
        }

        return null;
    }

    protected function resolveBlockingReasonKey(string $approval_status, string $compatibility_state, string $entitlement_state): ?string
    {
        if ($approval_status !== 'approved') {
            return 'package_unapproved';
        }

        if ($compatibility_state !== 'compatible' && $compatibility_state !== 'unknown') {
            return $compatibility_state;
        }

        if ($entitlement_state !== 'not_applicable' && $entitlement_state !== 'verified_active') {
            return $entitlement_state;
        }

        return null;
    }
}
