<?php

namespace PloverCRM\Common;

/**
 * License Checker
 * 
 * Handles license validation against plovercrm.com Hub.
 * Supports both subscription-based (OAuth) and promotional license keys.
 */
class LicenseChecker
{
    const HUB_URL_LIVE = 'https://plovercrm.com';
    const HUB_URL_LOCAL = 'http://plovercrm.local';
    const REVOKE_SIGNATURE_TTL = 300;
    const CALLBACK_QUERY_VAR = 'plovercrm_license_callback';
    const CALLBACK_STATE_QUERY_VAR = 'plovercrm_state';
    const CALLBACK_STATE_TRANSIENT_PREFIX = 'plover_crm_license_callback_state_';
    const CALLBACK_STATE_TTL = 30 * MINUTE_IN_SECONDS;

    /**
     * Get the hub URL — auto-detects local vs production
     */
    public static function getHubUrl(): string
    {
        $host = parse_url(home_url(), PHP_URL_HOST) ?? '';

        if (str_ends_with($host, '.local') || str_ends_with($host, '.test') || str_contains($host, 'localhost')) {
            return self::HUB_URL_LOCAL;
        }

        return self::HUB_URL_LIVE;
    }
    const OPTION_KEY = 'plover_crm_license';
    const TRANSIENT_KEY = 'plover_crm_license_valid';
    const CACHE_DURATION = 12 * HOUR_IN_SECONDS; // 12 hours
    private static ?array $requestStatus = null;

    /**
     * Get the canonical license status payload shared across admin, hosted, and standalone surfaces.
     */
    public static function getStatus(): array
    {
        if (self::$requestStatus !== null) {
            return self::$requestStatus;
        }

        $license = self::getLicense();

        if (empty($license['token'])) {
            return self::rememberStatus(self::buildStatus('inactive', false, $license));
        }

        if (self::isExpiredDate((string) ($license['expires'] ?? ''))) {
            delete_transient(self::TRANSIENT_KEY);
            return self::rememberStatus(self::buildStatus('expired', false, $license));
        }

        if (get_transient(self::TRANSIENT_KEY) === 'valid') {
            return self::rememberStatus(self::buildStatus('active', true, $license));
        }

        $result = self::validateRemote((string) $license['token']);

        if ($result && !empty($result['valid'])) {
            $license = array_merge($license, [
                'plan' => $result['plan'] ?? $license['plan'] ?? '',
                'expires' => $result['expires'] ?? $license['expires'] ?? '',
                'entitlements' => is_array($result['entitlements'] ?? null) ? $result['entitlements'] : ($license['entitlements'] ?? []),
                'dashboard_url' => $result['dashboard_url'] ?? $license['dashboard_url'] ?? '',
            ]);

            self::updateLicense([
                'plan' => $license['plan'],
                'expires' => $license['expires'],
                'entitlements' => $license['entitlements'],
                'dashboard_url' => $license['dashboard_url'],
            ]);
            set_transient(self::TRANSIENT_KEY, 'valid', self::CACHE_DURATION);
            return self::rememberStatus(self::buildStatus('active', true, $license));
        }

        delete_transient(self::TRANSIENT_KEY);
        return self::rememberStatus(self::buildStatus(
            self::resolveInactiveState($result, $license),
            false,
            $license
        ));
    }

    /**
     * Check if current site has a valid license.
     */
    public static function isValid(): bool
    {
        return self::getStatus()['is_active'];
    }

    /**
     * Get stored license data
     */
    public static function getLicense(): array
    {
        return get_option(self::OPTION_KEY, [
            'token'           => '',
            'plan'            => '',
            'type'            => '', // 'subscription' or 'promo'
            'subscription_id' => '',
            'expires'         => '',
            'activated_at'    => '',
            'entitlements'    => [],
            'dashboard_url'   => '',
        ]);
    }

    /**
     * Update license data (partial update)
     */
    public static function updateLicense(array $data): void
    {
        $current = self::getLicense();
        update_option(self::OPTION_KEY, array_merge($current, $data));
        self::$requestStatus = null;
    }

    /**
     * Store a new license activation
     */
    public static function activate(string $token, array $meta = []): void
    {
        update_option(self::OPTION_KEY, array_merge([
            'token'           => $token,
            'plan'            => $meta['plan'] ?? '',
            'type'            => $meta['type'] ?? 'subscription',
            'subscription_id' => $meta['subscription_id'] ?? '',
            'expires'         => $meta['expires'] ?? '',
            'activated_at'    => current_time('mysql'),
            'entitlements'    => is_array($meta['entitlements'] ?? null) ? $meta['entitlements'] : [],
        ], $meta));

        set_transient(self::TRANSIENT_KEY, 'valid', self::CACHE_DURATION);
        self::$requestStatus = null;
    }

    /**
     * Clear the locally stored license and related cache.
     */
    public static function clearLocalLicense(): void
    {
        delete_option(self::OPTION_KEY);
        delete_transient(self::TRANSIENT_KEY);
        self::$requestStatus = null;
    }

    /**
     * Immediately revoke the local license when the hub confirms a disconnect.
     */
    public static function revokeFromHub(string $token): bool
    {
        $license = self::getLicense();
        $storedToken = (string) ($license['token'] ?? '');

        if ($storedToken === '' || !hash_equals($storedToken, $token)) {
            return false;
        }

        self::clearLocalLicense();

        return true;
    }

    /**
     * Create the signed payload hash used by hub-initiated revoke requests.
     */
    public static function buildHubRevokeSignature(string $token, int $timestamp): string
    {
        return hash_hmac('sha256', 'license_revoke|' . $token . '|' . $timestamp, $token);
    }

    /**
     * Verify a hub-initiated revoke request signature.
     */
    public static function verifyHubRevokeSignature(string $token, int $timestamp, string $signature): bool
    {
        if ($timestamp <= 0 || abs(time() - $timestamp) > self::REVOKE_SIGNATURE_TTL) {
            return false;
        }

        $expected = self::buildHubRevokeSignature($token, $timestamp);

        return hash_equals($expected, $signature);
    }

    /**
     * Deactivate the license (disconnect from hub)
     */
    public static function deactivate(): bool
    {
        $license = self::getLicense();

        if (!empty($license['token'])) {
            // Notify hub
            wp_remote_post(self::getHubUrl() . '/wp-json/plovercrm-hub/v1/license/deactivate', [
                'body'    => ['token' => $license['token']],
                'timeout' => 10,
            ]);
        }

        self::clearLocalLicense();

        return true;
    }

    /**
     * Validate token against hub API
     */
    public static function validateRemote(string $token): ?array
    {
        $response = wp_remote_post(self::getHubUrl() . '/wp-json/plovercrm-hub/v1/license/validate', [
            'body'    => ['token' => $token],
            'timeout' => 15,
        ]);

        if (is_wp_error($response)) {
            // Network error — be lenient, assume valid for now
            return ['valid' => true];
        }

        $body = json_decode(wp_remote_retrieve_body($response), true);
        return is_array($body) ? $body : null;
    }

    /**
     * Activate via promotional license key
     */

    /**
     * Get the customer dashboard URL on the hub
     */
    public static function getDashboardUrl(): string
    {
        $license = self::getLicense();
        return !empty($license['dashboard_url']) ? $license['dashboard_url'] : self::getHubUrl() . '/account/';
    }

    /**
     * Cache the resolved status for the current PHP request.
     */
    private static function rememberStatus(array $status): array
    {
        self::$requestStatus = $status;

        return $status;
    }

    /**
     * Build the normalized status payload exposed to app clients.
     */
    private static function buildStatus(string $state, bool $isActive, array $license): array
    {
        return [
            'is_active' => $isActive,
            'state' => $state,
            'label' => match ($state) {
                'active' => 'Active',
                'expired' => 'Expired',
                default => 'Inactive',
            },
            'message' => self::getStatusMessage($state),
            'plan' => (string) ($license['plan'] ?? ''),
            'expires' => (string) ($license['expires'] ?? ''),
            'manage_url' => admin_url('admin.php?page=plover-crm-license'),
            'dashboard_url' => self::getDashboardUrl(),
        ];
    }

    /**
     * Resolve a stable, user-facing license state from the latest validation result.
     */
    private static function resolveInactiveState(?array $result, array $license): string
    {
        $code = sanitize_key((string) ($result['code'] ?? ''));
        $error = strtolower((string) ($result['error'] ?? ''));

        if ($code === 'subscription_expired' || str_contains($error, 'expired')) {
            return 'expired';
        }

        if (self::isExpiredDate((string) ($license['expires'] ?? ''))) {
            return 'expired';
        }

        return 'inactive';
    }

    /**
     * Provide a single source of truth for license copy across the app surfaces.
     */
    private static function getStatusMessage(string $state): string
    {
        return match ($state) {
            'active' => '',
            'expired' => 'The PloverCRM license for this site has expired. Ask the site administrator to renew or reactivate it.',
            default => 'The PloverCRM license is not active on this site. Ask the site administrator to activate it.',
        };
    }

    /**
     * Check whether the stored expiration timestamp has already passed.
     */
    private static function isExpiredDate(string $expires): bool
    {
        if ($expires === '') {
            return false;
        }

        $timestamp = strtotime($expires);

        if ($timestamp === false) {
            return false;
        }

        return $timestamp <= current_time('timestamp', true);
    }

    /**
     * Get the normalized entitlement snapshot for this site license.
     */
    public static function getEntitlements(): array
    {
        $license = self::getLicense();
        $entitlements = $license['entitlements'] ?? [];

        return is_array($entitlements) ? $entitlements : [];
    }

    /**
     * Get a single entitlement value.
     */
    public static function getEntitlement(string $key, ?int $default = null): ?int
    {
        $entitlements = self::getEntitlements();

        if (!isset($entitlements[$key])) {
            return $default;
        }

        $value = (int) $entitlements[$key];

        return $value > 0 ? $value : $default;
    }

    /**
     * Get the CRM user seat limit for this licensed site.
     */
    public static function getCrmUserLimit(): ?int
    {
        $limit = self::getEntitlement('crm_user_limit');

        if ($limit === null || $limit <= 0) {
            return null;
        }

        return $limit;
    }

    /**
     * Get CRM user seat usage for the current site.
     */
    public static function getCrmUserSeatUsage(): array
    {
        $limit = self::getCrmUserLimit();
        $used = RoleManager::countAssignedUsers();
        $available = $limit === null ? null : max(0, $limit - $used);

        return [
            'used' => $used,
            'limit' => $limit,
            'available' => $available,
            'is_limited' => $limit !== null,
            'is_over_limit' => $limit !== null && $used > $limit,
            'can_add_more' => $limit === null || $used < $limit,
        ];
    }

    /**
     * Check whether another CRM user seat can be assigned.
     */
    public static function canAssignAnotherCrmUser(): bool
    {
        $usage = self::getCrmUserSeatUsage();

        return $usage['can_add_more'];
    }

    /**
     * Get the OAuth authorization URL to redirect user to plovercrm.com
     */
    public static function getAuthUrl(): string
    {
        $domain = parse_url(home_url(), PHP_URL_HOST);
        $siteUrl = home_url();
        $redirectUri = self::getCallbackUrl();

        return self::getHubUrl() . '/plovercrm-activate/?' . http_build_query([
            'redirect_uri' => $redirectUri,
            'domain'       => $domain,
            'site_url'     => $siteUrl,
        ]);
    }

    /**
     * Build the local front-end callback URL used after hub authorization.
     */
    public static function getCallbackUrl(): string
    {
        return add_query_arg([
            self::CALLBACK_QUERY_VAR => '1',
            self::CALLBACK_STATE_QUERY_VAR => self::createCallbackState(),
        ], home_url('/'));
    }

    /**
     * Validate and consume a one-time callback state token.
     */
    public static function consumeCallbackState(string $state): bool
    {
        if ($state === '') {
            return false;
        }

        $transientKey = self::CALLBACK_STATE_TRANSIENT_PREFIX . $state;
        $isValid = (bool) get_transient($transientKey);
        delete_transient($transientKey);

        return $isValid;
    }

    /**
     * Resolve the post-activation redirect target back to the customer site.
     */
    public static function getPostActivationUrl(array $params = []): string
    {
        return admin_url('admin.php?page=plover-crm-license&activated=1');
    }

    /**
     * Handle the OAuth callback — complete activation
     */
    public static function handleCallback(array $params): array
    {
        $subscriptionId = (int) ($params['subscription_id'] ?? 0);
        $nonce = sanitize_text_field($params['nonce'] ?? '');
        $domain = sanitize_text_field($params['domain'] ?? '');
        $siteUrl = esc_url_raw($params['site_url'] ?? '');

        if (!$subscriptionId || empty($nonce)) {
            return ['success' => false, 'error' => 'Invalid callback parameters.'];
        }

        $response = wp_remote_post(self::getHubUrl() . '/wp-json/plovercrm-hub/v1/license/activate', [
            'body' => [
                'subscription_id' => $subscriptionId,
                'nonce'           => $nonce,
                'domain'          => $domain,
                'site_url'        => $siteUrl,
            ],
            'timeout' => 15,
        ]);

        if (is_wp_error($response)) {
            return ['success' => false, 'error' => 'Could not connect to PloverCRM. Please try again.'];
        }

        $body = json_decode(wp_remote_retrieve_body($response), true);

        if (!empty($body['success']) && !empty($body['token'])) {
            self::activate($body['token'], [
                'type'            => 'subscription',
                'subscription_id' => $subscriptionId,
                'plan'            => $body['plan'] ?? '',
                'expires'         => $body['expires'] ?? '',
                'entitlements'    => is_array($body['entitlements'] ?? null) ? $body['entitlements'] : [],
                'dashboard_url'   => $body['dashboard_url'] ?? '',
            ]);
            return ['success' => true, 'message' => $body['message'] ?? 'Site activated.'];
        }

        return ['success' => false, 'error' => $body['error'] ?? 'Activation failed.'];
    }

    /**
     * Create and persist a one-time state token for the activation callback.
     */
    private static function createCallbackState(): string
    {
        $state = wp_generate_password(32, false, false);
        set_transient(self::CALLBACK_STATE_TRANSIENT_PREFIX . $state, 1, self::CALLBACK_STATE_TTL);

        return $state;
    }
}
