<?php

namespace GriffinForms\Includes\Packages;

use GriffinForms\Traits\Singleton;

class PackageEntitlement extends \GriffinForms\Format
{
    use Singleton;

    protected $store;

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

    public function getPackageRecord(string $package_id): array
    {
        return $this->sanitizeRecord($this->store->getPackageEntitlementState($package_id));
    }

    public function savePackageRecord(string $package_id, array $record): bool
    {
        $package_id = sanitize_key($package_id);
        if ($package_id === '') {
            return false;
        }

        return $this->store->savePackageEntitlementState($package_id, $this->sanitizeRecord($record));
    }

    public function markKeyEntered(string $package_id): bool
    {
        $record = $this->getPackageRecord($package_id);
        $record['license_key_present'] = true;
        $record['license_last_action'] = 'key_entered';

        if (($record['verification_state'] ?? '') === 'key_not_entered') {
            $record['verification_state'] = 'unverified_connection_issue';
        }

        return $this->savePackageRecord($package_id, $record);
    }

    public function markVerifiedActive(string $package_id, array $args = []): bool
    {
        $record = $this->getPackageRecord($package_id);
        $record['license_key_present'] = true;
        $record['verification_state'] = 'verified_active';
        $record['last_verified_at'] = gmdate('c');
        $record['last_successful_verification_at'] = gmdate('c');
        $record['failure_count'] = 0;
        $record['next_retry_at'] = '';
        $record['grace_started_at'] = '';
        $record['grace_expires_at'] = '';
        $record['license_last_action'] = 'verified_active';
        $record['license_expires_at'] = $this->sanitizeTimestamp($args['license_expires_at'] ?? '');

        return $this->savePackageRecord($package_id, $record);
    }

    public function markVerifiedInactiveOrExpired(string $package_id, array $args = []): bool
    {
        $record = $this->getPackageRecord($package_id);
        $record['license_key_present'] = !array_key_exists('license_key_present', $args) || !empty($args['license_key_present']);
        $record['verification_state'] = 'verified_inactive_or_expired';
        $record['last_verified_at'] = gmdate('c');
        $record['license_last_action'] = 'verified_inactive_or_expired';
        $record['license_expires_at'] = $this->sanitizeTimestamp($args['license_expires_at'] ?? '');

        return $this->savePackageRecord($package_id, $record);
    }

    public function markConnectionIssue(string $package_id, array $args = []): bool
    {
        $record = $this->getPackageRecord($package_id);
        $record['license_key_present'] = !empty($record['license_key_present']) || !empty($args['license_key_present']);
        $record['verification_state'] = 'unverified_connection_issue';
        $record['last_verified_at'] = gmdate('c');
        $record['last_failed_verification_at'] = gmdate('c');
        $record['failure_count'] = max(0, absint($record['failure_count'] ?? 0)) + 1;
        $record['next_retry_at'] = $this->sanitizeTimestamp($args['next_retry_at'] ?? '');
        $record['license_last_action'] = 'verification_failed';

        $grace_expires_at = $this->sanitizeTimestamp($args['grace_expires_at'] ?? '');
        if ($grace_expires_at !== '') {
            $record['grace_started_at'] = gmdate('c');
            $record['grace_expires_at'] = $grace_expires_at;
        }

        return $this->savePackageRecord($package_id, $record);
    }

    public function clearPackageRecord(string $package_id): bool
    {
        return $this->savePackageRecord($package_id, []);
    }

    public function resolveState(array $package, bool $is_present, bool $is_active): array
    {
        $package_id = sanitize_key((string) ($package['package_id'] ?? ''));
        $supports_entitlement = !empty($package['supports_entitlement']);
        $record = $this->getPackageRecord($package_id);
        $now = time();

        if (!$supports_entitlement) {
            return $this->buildResolvedState('not_applicable', $record);
        }

        if (!$is_present) {
            return $this->buildResolvedState('not_installed', $record);
        }

        if (!$is_active) {
            return $this->buildResolvedState('inactive', $record);
        }

        if (empty($record['license_key_present'])) {
            return $this->buildResolvedState('key_not_entered', $record);
        }

        $verification_state = (string) ($record['verification_state'] ?? 'key_not_entered');
        if ($verification_state === 'verified_active') {
            return $this->buildResolvedState('verified_active', $record);
        }

        if ($verification_state === 'verified_inactive_or_expired') {
            return $this->buildResolvedState('verified_inactive_or_expired', $record);
        }

        if ($verification_state === 'unverified_connection_issue') {
            if ($this->isGraceActive($record, $now)) {
                return $this->buildResolvedState('grace_period', $record);
            }

            return $this->buildResolvedState('unverified_connection_issue', $record);
        }

        return $this->buildResolvedState('key_not_entered', $record);
    }

    protected function buildResolvedState(string $entitlement_state, array $record): array
    {
        return [
            'entitlement_state' => $entitlement_state,
            'license_key_present' => !empty($record['license_key_present']),
            'verification_state' => (string) ($record['verification_state'] ?? ''),
            'last_verified_at' => $this->sanitizeTimestamp($record['last_verified_at'] ?? ''),
            'last_successful_verification_at' => $this->sanitizeTimestamp($record['last_successful_verification_at'] ?? ''),
            'last_failed_verification_at' => $this->sanitizeTimestamp($record['last_failed_verification_at'] ?? ''),
            'next_retry_at' => $this->sanitizeTimestamp($record['next_retry_at'] ?? ''),
            'grace_started_at' => $this->sanitizeTimestamp($record['grace_started_at'] ?? ''),
            'grace_expires_at' => $this->sanitizeTimestamp($record['grace_expires_at'] ?? ''),
            'license_expires_at' => $this->sanitizeTimestamp($record['license_expires_at'] ?? ''),
            'license_last_action' => sanitize_key((string) ($record['license_last_action'] ?? '')),
            'failure_count' => max(0, absint($record['failure_count'] ?? 0)),
        ];
    }

    protected function sanitizeRecord(array $record): array
    {
        $allowed_states = [
            'key_not_entered',
            'verified_active',
            'verified_inactive_or_expired',
            'unverified_connection_issue',
        ];

        $verification_state = sanitize_key((string) ($record['verification_state'] ?? 'key_not_entered'));
        if (!in_array($verification_state, $allowed_states, true)) {
            $verification_state = 'key_not_entered';
        }

        return [
            'license_key_present' => !empty($record['license_key_present']),
            'verification_state' => $verification_state,
            'last_verified_at' => $this->sanitizeTimestamp($record['last_verified_at'] ?? ''),
            'last_successful_verification_at' => $this->sanitizeTimestamp($record['last_successful_verification_at'] ?? ''),
            'last_failed_verification_at' => $this->sanitizeTimestamp($record['last_failed_verification_at'] ?? ''),
            'next_retry_at' => $this->sanitizeTimestamp($record['next_retry_at'] ?? ''),
            'grace_started_at' => $this->sanitizeTimestamp($record['grace_started_at'] ?? ''),
            'grace_expires_at' => $this->sanitizeTimestamp($record['grace_expires_at'] ?? ''),
            'license_expires_at' => $this->sanitizeTimestamp($record['license_expires_at'] ?? ''),
            'license_last_action' => sanitize_key((string) ($record['license_last_action'] ?? '')),
            'failure_count' => max(0, absint($record['failure_count'] ?? 0)),
        ];
    }

    protected function sanitizeTimestamp($value): string
    {
        $value = trim((string) $value);
        if ($value === '') {
            return '';
        }

        $timestamp = strtotime($value);
        return $timestamp !== false ? gmdate('c', $timestamp) : '';
    }

    protected function isGraceActive(array $record, int $now): bool
    {
        $grace_expires_at = $this->sanitizeTimestamp($record['grace_expires_at'] ?? '');
        if ($grace_expires_at === '') {
            return false;
        }

        $grace_timestamp = strtotime($grace_expires_at);
        return $grace_timestamp !== false && $grace_timestamp >= $now;
    }
}
