<?php

declare(strict_types=1);

namespace FormConnector\Features\License;

/**
 * License management class.
 * Handles license validation, storage, and status checks.
 */
class License {

	private const OPTION_KEY = 'fc_license_key';
	private const DATA_OPTION = 'fc_license_data';
	private const TRANSIENT_KEY = 'fc_license_cache';
	private const CACHE_DURATION = 300; // 5 minutes
	private const VALIDATION_CACHE_KEY = 'fc_license_validated';
	private const VALIDATION_CACHE_DURATION = 3600; // 1 hour - how often to re-verify with API

	/**
	 * API endpoint for license validation.
	 */
	private string $api_endpoint;

	/**
	 * Cached license data.
	 */
	private ?array $data = null;

	/**
	 * Constructor.
	 */
	public function __construct() {
		$this->api_endpoint = defined('FC_LICENSE_API_ENDPOINT') 
			? FC_LICENSE_API_ENDPOINT 
			: 'https://app.formconnector.co.uk';
	}

	/**
	 * Get the stored license key.
	 */
	public function get_key(): string {
		return get_option(self::OPTION_KEY, '');
	}

	/**
	 * Save a license key.
	 */
	public function save_key(string $key): bool {
		$key = sanitize_text_field(trim($key));
		return update_option(self::OPTION_KEY, $key);
	}

	/**
	 * Remove the license key.
	 */
	public function remove_key(): bool {
		delete_option(self::DATA_OPTION);
		delete_transient(self::TRANSIENT_KEY);
		return delete_option(self::OPTION_KEY);
	}

	/**
	 * Get license data.
	 */
	public function get_data(bool $force_refresh = false): array {
		// Return cached instance data if available
		if (!$force_refresh && $this->data !== null) {
			return $this->data;
		}

		// Try transient cache first
		if (!$force_refresh) {
			$cached = get_transient(self::TRANSIENT_KEY);
			if ($cached !== false && is_array($cached)) {
				$this->data = $cached;
				return $this->data;
			}
		}

		// Load from database
		$stored = get_option(self::DATA_OPTION, []);
		$this->data = is_array($stored) ? $stored : [];

		return $this->data;
	}

	/**
	 * Store license data.
	 */
	private function store_data(array $data): void {
		$data['last_checked'] = current_time('mysql', true);
		
		update_option(self::DATA_OPTION, $data);
		set_transient(self::TRANSIENT_KEY, $data, self::CACHE_DURATION);
		
		$this->data = $data;
	}

	/**
	 * Check if the license is active.
	 */
	public function is_active(bool $force_refresh = false): bool {
		$data = $this->get_data($force_refresh);
		
		if (empty($data)) {
			return false;
		}

		$status = $data['status'] ?? '';
		
		return in_array($status, ['active', 'valid'], true);
	}

	/**
	 * Check if the license is expired.
	 */
	public function is_expired(): bool {
		$data = $this->get_data();
		
		if (empty($data['expires_at'])) {
			return false;
		}

		$expires = strtotime($data['expires_at']);
		
		return $expires !== false && $expires < time();
	}

	/**
	 * Get the current plan name.
	 */
	public function get_plan_name(): string {
		$data = $this->get_data();
		
		if (!$this->is_active()) {
			return __('Free Plan', 'form-connector');
		}

		return $data['plan_name'] ?? $data['planName'] ?? __('Premium', 'form-connector');
	}

	/**
	 * Get form limit for current plan.
	 * Returns null for unlimited.
	 */
	public function get_form_limit(): ?int {
		$data = $this->get_data();
		
		if (!$this->is_active()) {
			return 3; // Free tier limit
		}

		// Check various field names
		$limit = $data['form_limit'] ?? $data['formLimit'] ?? null;
		
		if ($limit === null || $limit === 'unlimited' || $limit === -1) {
			return null; // Unlimited
		}

		return (int) $limit;
	}

	/**
	 * Check if license is valid for protected operations.
	 * Uses longer cache to avoid hammering the license server.
	 * 
	 * @param bool $force_refresh Force a fresh check with the API.
	 * @return array{valid: bool, error?: string, code?: string}
	 */
	public function check_license(bool $force_refresh = false): array {
		$key = $this->get_key();

		// No key configured
		if (empty($key)) {
			return [
				'valid' => false,
				'error' => __('No license key configured. Please add your API key.', 'form-connector'),
				'code' => 'no_license',
			];
		}

		// Check validation cache first (longer cache for performance)
		if (!$force_refresh) {
			$validation_cache = get_transient(self::VALIDATION_CACHE_KEY);
			if ($validation_cache !== false && is_array($validation_cache)) {
				// Return cached validation result
				if ($validation_cache['valid'] ?? false) {
					return ['valid' => true];
				} else {
					return [
						'valid' => false,
						'error' => $validation_cache['error'] ?? __('License is not active.', 'form-connector'),
						'code' => $validation_cache['code'] ?? 'invalid_license',
					];
				}
			}
		}

		// Check stored data (shorter cache)
		$data = $this->get_data($force_refresh);
		
		if (!empty($data) && $this->is_active()) {
			// Cache the valid state
			set_transient(self::VALIDATION_CACHE_KEY, ['valid' => true], self::VALIDATION_CACHE_DURATION);
			return ['valid' => true];
		}

		// If we have a key but no valid cached data, re-validate with API
		$result = $this->validate($key);

		if ($result['success']) {
			set_transient(self::VALIDATION_CACHE_KEY, ['valid' => true], self::VALIDATION_CACHE_DURATION);
			return ['valid' => true];
		}

		// Cache the failure
		$failure = [
			'valid' => false,
			'error' => $result['error'] ?? __('License validation failed.', 'form-connector'),
			'code' => 'validation_failed',
		];
		set_transient(self::VALIDATION_CACHE_KEY, $failure, 300); // Cache failures for 5 min only
		
		return $failure;
	}

	/**
	 * Require an active license for an operation.
	 * Returns a WP_REST_Response error if license is not valid.
	 * 
	 * @return \WP_REST_Response|null Null if valid, error response if not.
	 */
	public function require_license(): ?\WP_REST_Response {
		$check = $this->check_license();

		if ($check['valid']) {
			return null; // License is valid, proceed
		}

		return new \WP_REST_Response([
			'success' => false,
			'license_error' => true,
			'code' => $check['code'] ?? 'license_required',
			'message' => $check['error'] ?? __('A valid license is required for this action.', 'form-connector'),
		], 403);
	}

	/**
	 * Clear the validation cache (useful after activating/deactivating).
	 */
	public function clear_validation_cache(): void {
		delete_transient(self::VALIDATION_CACHE_KEY);
	}

	/**
	 * Validate a license key with the API.
	 */
	public function validate(string $key): array {
		$key = sanitize_text_field(trim($key));

		if (empty($key)) {
			return [
				'success' => false,
				'error' => __('License key cannot be empty.', 'form-connector'),
			];
		}

		// Make API request
		$response = $this->make_api_request($key);

		if (is_wp_error($response)) {
			error_log(sprintf('[FormConnector] License API network error: %s', $response->get_error_message()));
			return [
				'success' => false,
				'error' => $response->get_error_message(),
			];
		}

		$code = wp_remote_retrieve_response_code($response);
		$body = wp_remote_retrieve_body($response);
		$data = json_decode($body, true);

		// Log non-success responses for debugging (avoid logging successful validations)
		if ($code < 200 || $code >= 300) {
			error_log(sprintf('[FormConnector] License validation failed (HTTP %d): %s', $code, $body));
		}

		// Handle non-200 responses
		if ($code < 200 || $code >= 300) {
			$error_message = $this->get_error_message($code, $data);
			
			$this->store_data([
				'key' => $key,
				'status' => 'error',
				'error_code' => $code,
				'error_message' => $error_message,
			]);

			return [
				'success' => false,
				'error' => $error_message,
			];
		}

		// Check if API returned success: false
		if (isset($data['success']) && $data['success'] === false) {
			$error_message = $data['message'] ?? $data['error'] ?? __('Invalid license key.', 'form-connector');
			
			$this->store_data([
				'key' => $key,
				'status' => 'invalid',
				'error_message' => $error_message,
			]);

			return [
				'success' => false,
				'error' => $error_message,
			];
		}

		// Parse successful response
		$license_data = $this->parse_api_response($data, $key);
		
		// Save the key and data
		$this->save_key($key);
		$this->store_data($license_data);

		// Register/refresh this site with CareHQ so the two-way connection is alive
		// and we receive a signing secret for subsequent signed requests.
		if (class_exists('\\FormConnector\\Health\\HealthPing')) {
			\FormConnector\Integration\Health\HealthPing::send_ping();
		}

		return [
			'success' => true,
			'data' => $license_data,
		];
	}

	/**
	 * Deactivate the current license.
	 */
	public function deactivate(): array {
		$key = $this->get_key();

		if (empty($key)) {
			return [
				'success' => false,
				'error' => __('No license key to deactivate.', 'form-connector'),
			];
		}

		// Optionally call API to deactivate
		// For now, just clear local data
		$this->remove_key();

		return [
			'success' => true,
			'message' => __('License deactivated successfully.', 'form-connector'),
		];
	}

	/**
	 * Get license status for REST API/frontend.
	 */
	public function get_status(): array {
		$key = $this->get_key();
		$data = $this->get_data();
		$is_active = $this->is_active();
		$is_expired = $this->is_expired();

		// Mask the API key for display (show first 8 and last 4 chars)
		$masked_key = '';
		if (!empty($key)) {
			$len = strlen($key);
			if ($len > 12) {
				$masked_key = substr($key, 0, 8) . '...' . substr($key, -4);
			} else {
				$masked_key = str_repeat('•', $len);
			}
		}

		// Fetch subscription info if active
		$subscription = [];
		if ($is_active) {
			$subscription = $this->fetch_subscription_info();
		}

		// Enrich plugin_update with the server-controlled auto-update preference
		$plugin_update = get_option(\FormConnector\Integration\CareHQ\Signing::OPTION_PLUGIN_UPDATE, null);
		if (is_array($plugin_update)) {
			$plugin_update['auto_update_enabled'] = get_option('fc_plugin_auto_update_enabled', null);
		}

		return [
			'has_key' => !empty($key),
			'masked_key' => $masked_key,
			'is_active' => $is_active,
			'is_expired' => $is_expired,
			'status' => $data['status'] ?? 'not_configured',
			'plan_name' => $subscription['plan'] ?? $this->get_plan_name(),
			'plan_id' => $data['plan_id'] ?? ($is_active ? 'premium' : 'free'),
			'form_limit' => $this->get_form_limit(),
			'expires_at' => $data['expires_at'] ?? null,
			'last_checked' => $data['last_checked'] ?? null,
			'error_message' => $data['error_message'] ?? null,
			'api_key_name' => $data['api_key_name'] ?? null,
			'user_id' => $data['user_id'] ?? null,
			// Subscription/seats info
			'total_seats' => $subscription['total_seats'] ?? null,
			'used_seats' => $subscription['used_seats'] ?? null,
			'available_seats' => $subscription['available_seats'] ?? null,
			'is_unlimited' => $subscription['is_unlimited'] ?? false,
			'trial_consumed_at' => $subscription['trial_consumed_at'] ?? null,
			'subscription_status' => $subscription['subscription_status'] ?? null,
			'pending_downgrade' => $subscription['pending_downgrade'] ?? null,
			'plugin_update' => $plugin_update,
			'plugin_version' => defined('FC_VERSION') ? FC_VERSION : '0.0.0',
			'plugin_health_status' => get_option(\FormConnector\Integration\CareHQ\Signing::OPTION_HEALTH_STATUS, 'unknown'),
			'plugin_last_ping' => get_option(\FormConnector\Integration\CareHQ\Signing::OPTION_LAST_PING, null),
			'webhook_url' => \FormConnector\Integration\Health\HealthPing::webhook_url(),
			'has_signing_secret' => \FormConnector\Integration\CareHQ\Signing::get_secret() !== '',
			'homes_count' => $subscription['homes_count'] ?? 0,
		];
	}

	/**
	 * Make an API request to validate a license/API key.
	 * Uses POST /api/validate with x-api-key header per the CareHQ API spec.
	 */
	private function make_api_request(string $key): array|\WP_Error {
		// API endpoint: POST https://app.formconnector.co.uk/api/validate
		$url = rtrim($this->api_endpoint, '/') . '/api/validate';

		return wp_remote_post($url, [
			'timeout' => 30,
			'headers' => [
				'x-api-key' => $key,
				'Content-Type' => 'application/json',
				'Accept' => 'application/json',
			],
			'body' => wp_json_encode([]),
		]);
	}

	/**
	 * Fetch subscription info from /api/homes endpoint.
	 * Returns subscription details including seats.
	 */
	public function fetch_subscription_info(): array {
		$key = $this->get_key();
		
		if (empty($key)) {
			return [];
		}

		$url = rtrim($this->api_endpoint, '/') . '/api/homes';
		
		$response = wp_remote_get($url, [
			'timeout' => 30,
			'headers' => [
				'x-api-key' => $key,
				'Accept' => 'application/json',
			],
		]);

		if (is_wp_error($response)) {
			return [];
		}

		$code = wp_remote_retrieve_response_code($response);
		if ($code < 200 || $code >= 300) {
			return [];
		}

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

		if (!is_array($data) || empty($data['success'])) {
			return [];
		}

		// Extract subscription info
		$subscription = $data['data']['subscription'] ?? [];
		$homes = $data['data']['homes'] ?? [];
		$subscription = \FormConnector\Features\Homes\Homes::normalize_subscription($subscription, count($homes)) ?? [];

		return [
			'total_seats' => $subscription['totalSeats'] ?? null,
			'used_seats' => $subscription['usedSeats'] ?? null,
			'available_seats' => $subscription['availableSeats'] ?? null,
			'is_unlimited' => (bool) ($subscription['isUnlimited'] ?? false),
			'trial_consumed_at' => $subscription['trialConsumedAt'] ?? null,
			'pending_downgrade' => $subscription['pendingDowngrade'] ?? null,
			'plan' => $subscription['plan'] ?? null,
			'subscription_status' => $subscription['status'] ?? null,
			'homes_count' => count($homes),
		];
	}

	/**
	 * Parse API response into a normalized structure.
	 * Handles the /api/validate response format.
	 */
	private function parse_api_response(array $response, string $key): array {
		// Handle nested data structure from /api/validate
		$data = $response['data'] ?? $response;
		$success = $response['success'] ?? false;

		// If API returned success: true, the key is valid/active
		$status = $success ? 'active' : 'invalid';

		// Build normalized license data
		return [
			'key' => $key,
			'status' => $status,
			'user_id' => $data['userId'] ?? null,
			'api_key_id' => $data['apiKeyId'] ?? null,
			'api_key_name' => $data['apiKeyName'] ?? null,
			'plan_id' => $data['plan_id'] ?? $data['productId'] ?? 'premium',
			'plan_name' => $data['plan_name'] ?? $data['planName'] ?? 'Premium',
			'form_limit' => $data['form_limit'] ?? $data['formLimit'] ?? null,
			'expires_at' => $data['expires_at'] ?? $data['expiresAt'] ?? null,
			'valid_for' => $data['valid_for'] ?? $data['validFor'] ?? null,
			'trial_consumed_at' => $data['trial_consumed_at'] ?? $data['trialConsumedAt'] ?? null,
			'times_activated' => $data['times_activated'] ?? $data['timesActivated'] ?? null,
			'max_activations' => $data['times_activated_max'] ?? $data['timesActivatedMax'] ?? null,
		];
	}

	/**
	 * Normalize status from various API response formats.
	 */
	private function normalize_status(array $data): string {
		$status = $data['status'] ?? null;

		// Already a string status
		if (is_string($status)) {
			$status = strtolower($status);
			if (in_array($status, ['active', 'valid', 'pending', 'expired', 'inactive', 'disabled'])) {
				return $status === 'valid' ? 'active' : $status;
			}
		}

		// Numeric status
		if (is_numeric($status)) {
			$map = [
				3 => 'active',
				2 => 'active',
				1 => 'pending',
				0 => 'inactive',
			];
			return $map[(int) $status] ?? 'unknown';
		}

		// Check for success flag
		if (isset($data['success']) && $data['success'] === true) {
			return 'active';
		}

		// Check for validFor field
		if (!empty($data['validFor']) || !empty($data['valid_for'])) {
			return 'active';
		}

		return 'unknown';
	}

	/**
	 * Get human-readable error message from API response.
	 */
	private function get_error_message(int $code, ?array $data): string {
		// Check for API error message
		if (!empty($data['message'])) {
			return sanitize_text_field($data['message']);
		}

		if (!empty($data['error'])) {
			return sanitize_text_field($data['error']);
		}

		// Default messages based on status code
		$messages = [
			400 => __('Invalid license key format.', 'form-connector'),
			401 => __('Unauthorized. Please check your license key.', 'form-connector'),
			403 => __('License key is not authorized for this site.', 'form-connector'),
			404 => __('License key not found.', 'form-connector'),
			429 => __('Too many requests. Please try again later.', 'form-connector'),
			500 => __('Server error. Please try again later.', 'form-connector'),
		];

		return $messages[$code] ?? __('License validation failed.', 'form-connector');
	}
}
