<?php

declare(strict_types=1);

namespace FormConnector\Features\Homes;

use FormConnector\Features\License\License;

/**
 * Homes Management
 * 
 * Manages care homes registered with the plugin license server.
 * Homes are linked to the user's subscription and consume seats.
 */
class Homes {

	/**
	 * HTTP status codes that indicate an API key / auth failure.
	 */
	private const AUTH_ERROR_CODES = [401, 403];

	/**
	 * Option key for locally cached homes
	 */
	private const OPTION_HOMES = 'fc_homes';

	/**
	 * Option key for user-defined home priority order
	 */
	private const OPTION_PRIORITY = 'fc_homes_priority';

	/**
	 * Cache transient key
	 */
	private const TRANSIENT_KEY = 'fc_homes_cache';
	private const CACHE_DURATION = 300; // 5 minutes

	/** Legacy backend sent very large totals for trial/unlimited seats. */
	private const LEGACY_UNLIMITED_SEAT_THRESHOLD = 9999;

	/**
	 * License instance
	 */
	private License $license;

	/**
	 * API endpoint base
	 */
	private string $api_endpoint;

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

	/**
	 * Normalize subscription seat fields from old and new backend responses.
	 */
	public static function normalize_subscription(?array $subscription, int $homes_count = 0): ?array {
		if ($subscription === null) {
			return null;
		}

		$total_seats = $subscription['totalSeats'] ?? $subscription['total_seats'] ?? null;
		$is_unlimited = (bool) (
			$subscription['isUnlimited']
			?? $subscription['isUnlimitedSeats']
			?? $subscription['hasUnlimitedSeats']
			?? $subscription['unlimitedSeats']
			?? false
		);

		if ($total_seats === null || ((int) $total_seats) >= self::LEGACY_UNLIMITED_SEAT_THRESHOLD) {
			$is_unlimited = true;
			$total_seats = null;
		} else {
			$total_seats = max(0, (int) $total_seats);
		}

		$used_seats = (int) ($subscription['usedSeats'] ?? $subscription['used_seats'] ?? $homes_count);
		$available_seats = $is_unlimited
			? null
			: max(0, (int) ($subscription['availableSeats'] ?? $subscription['available_seats'] ?? (($total_seats ?? 0) - $used_seats)));

		$subscription['totalSeats'] = $total_seats;
		$subscription['usedSeats'] = $used_seats;
		$subscription['availableSeats'] = $available_seats;
		$subscription['isUnlimited'] = $is_unlimited;
		$subscription['trialConsumedAt'] = $subscription['trialConsumedAt'] ?? $subscription['trial_consumed_at'] ?? null;

		if (isset($subscription['pendingDowngrade']) && is_array($subscription['pendingDowngrade'])) {
			$pending = $subscription['pendingDowngrade'];
			$pending['nextTotalSeats'] = $pending['nextTotalSeats'] ?? $pending['next_total_seats'] ?? null;
			$pending['effectiveAt'] = $pending['effectiveAt'] ?? $pending['effective_at'] ?? null;
			$pending['homesToRemove'] = $pending['homesToRemove'] ?? $pending['homes_to_remove'] ?? [];
			$subscription['pendingDowngrade'] = $pending;
		}

		return $subscription;
	}

	/**
	 * Get the finite seat limit from a normalized subscription. Null means unlimited.
	 */
	public static function get_seat_limit(?array $subscription): ?int {
		if (empty($subscription) || !empty($subscription['isUnlimited'])) {
			return null;
		}

		return isset($subscription['totalSeats']) ? (int) $subscription['totalSeats'] : null;
	}

	/**
	 * Get all homes and subscription info from the API
	 *
	 * @param bool $force_refresh Force refresh from API
	 */
	public function get_homes(bool $force_refresh = false): array {
		// Check if license is active
		if (!$this->license->is_active()) {
			return [
				'success' => false,
				'error' => 'license_inactive',
				'message' => __('License must be active to manage homes.', 'form-connector'),
			];
		}

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

		// Fetch from API
		$result = $this->fetch_homes_from_api();
		
		// Cache successful results
		if ($result['success']) {
			set_transient(self::TRANSIENT_KEY, $result, self::CACHE_DURATION);
		}

		return $result;
	}

	/**
	 * Sync homes to the API
	 *
	 * @param array $homes Array of homes to sync
	 */
	public function sync_homes(array $homes): array {
		if (!$this->license->is_active()) {
			return [
				'success' => false,
				'error' => 'license_inactive',
				'message' => __('License must be active to sync homes.', 'form-connector'),
			];
		}

		$key = $this->license->get_key();
		if (empty($key)) {
			return [
				'success' => false,
				'error' => 'no_license_key',
				'message' => __('No license key configured.', 'form-connector'),
			];
		}

		// Validate homes array
		$validated_homes = [];
		foreach ($homes as $home) {
			if (empty($home['homeId']) || empty($home['name'])) {
				continue;
			}
			$validated_homes[] = [
				'homeId' => sanitize_text_field($home['homeId']),
				'name' => sanitize_text_field($home['name']),
				'provider' => sanitize_text_field($home['provider'] ?? 'CareHQ'),
			];
		}

		if (empty($validated_homes)) {
			return [
				'success' => false,
				'error' => 'invalid_homes',
				'message' => __('No valid homes to sync.', 'form-connector'),
			];
		}

		// Make API request
		$url = rtrim($this->api_endpoint, '/') . '/api/homes/sync';

		// Build the body once and sign it — re-encoding after signing would
		// invalidate the HMAC. siteUrl is required for signed sync enforcement.
		$body = wp_json_encode([
			'siteUrl' => home_url(),
			'homes'   => $validated_homes,
		]);

		$response = wp_remote_post($url, [
			'timeout' => 30,
			'headers' => \FormConnector\Integration\CareHQ\Signing::build_signed_headers($body, $key),
			'body'    => $body,
		]);

		if (is_wp_error($response)) {
			return [
				'success' => false,
				'error' => 'network_error',
				'message' => $response->get_error_message(),
			];
		}

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

		// Log API response for debugging
		error_log(sprintf('[FormConnector] /api/homes/sync response (HTTP %d): %s', $code, $body));

		// Clear cache on any sync attempt
		delete_transient(self::TRANSIENT_KEY);

		if ($code === 403 && isset($data['error']) && $data['error'] === 'Insufficient seats') {
			return [
				'success' => false,
				'error' => 'insufficient_seats',
				'message' => $data['message'] ?? __('Not enough seats available.', 'form-connector'),
				'data' => $data['data'] ?? null,
			];
		}

		// Detect auth / API-key errors and invalidate license cache
		if ($this->is_auth_error($code, $data)) {
			return $this->auth_error_response($code, $data);
		}

		if ($code < 200 || $code >= 300) {
			return [
				'success' => false,
				'error' => $data['error'] ?? 'api_error',
				'message' => $data['message'] ?? __('Failed to sync homes.', 'form-connector'),
			];
		}

		// Check response body success flag (server may return 200 with success:false)
		if (isset($data['success']) && $data['success'] === false) {
			return [
				'success' => false,
				'error' => $data['error'] ?? 'api_error',
				'message' => $data['message'] ?? __('Failed to sync homes.', 'form-connector'),
			];
		}

		// Server may return success:true but with errors and created:0
		$errors_count = $data['data']['errors'] ?? 0;
		$created_count = $data['data']['created'] ?? 0;
		$updated_count = $data['data']['updated'] ?? 0;
		if ($errors_count > 0 && ($created_count + $updated_count) === 0) {
			$error_details = $data['data']['details']['errors'] ?? [];
			error_log(sprintf('[FormConnector] sync_homes reported success but all operations failed. Errors: %s', wp_json_encode($error_details)));

			return [
				'success' => false,
				'error' => 'server_sync_failed',
				'message' => __('The server failed to sync homes. Please try again or contact support.', 'form-connector'),
				'data' => $data['data'] ?? null,
			];
		}

		// Track last successful sync so future health pings can report it.
		update_option(\FormConnector\Integration\CareHQ\Signing::OPTION_LAST_SYNC, gmdate('c'), false);

		return [
			'success' => true,
			'message' => $data['message'] ?? __('Homes synced successfully.', 'form-connector'),
			'data' => $data['data'] ?? null,
		];
	}

	/**
	 * Remove a home by ID
	 *
	 * @param string $home_id The homeId to remove
	 */
	public function remove_home(string $home_id): array {
		if (empty($home_id)) {
			return [
				'success' => false,
				'error' => 'invalid_home_id',
				'message' => __('Home ID is required.', 'form-connector'),
			];
		}

		if (!$this->license->is_active()) {
			return [
				'success' => false,
				'error' => 'license_inactive',
				'message' => __('License must be active to remove homes.', 'form-connector'),
			];
		}

		$key = $this->license->get_key();
		if (empty($key)) {
			return [
				'success' => false,
				'error' => 'no_license_key',
				'message' => __('No license key configured.', 'form-connector'),
			];
		}

		// Clear cache before operation
		delete_transient(self::TRANSIENT_KEY);

		// Use the remove endpoint with homeIds array
		$url = rtrim($this->api_endpoint, '/') . '/api/homes/remove';

		$body = wp_json_encode([
			'siteUrl' => home_url(),
			'homeIds' => [$home_id],
		]);

		$response = wp_remote_post($url, [
			'timeout' => 30,
			'headers' => \FormConnector\Integration\CareHQ\Signing::build_signed_headers($body, $key),
			'body'    => $body,
		]);

		if (is_wp_error($response)) {
			return [
				'success' => false,
				'error' => 'network_error',
				'message' => $response->get_error_message(),
			];
		}

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

		// Log API response for debugging
		error_log(sprintf('[FormConnector] /api/homes/remove response (HTTP %d): %s', $code, $body));

		// Detect auth / API-key errors and invalidate license cache
		if ($this->is_auth_error($code, $data)) {
			return $this->auth_error_response($code, $data);
		}

		if ($code < 200 || $code >= 300) {
			return [
				'success' => false,
				'error' => $data['error'] ?? 'api_error',
				'message' => $data['message'] ?? __('Failed to remove home.', 'form-connector'),
			];
		}

		// Check response body success flag (server may return 200 with success:false)
		if (isset($data['success']) && $data['success'] === false) {
			return [
				'success' => false,
				'error' => $data['error'] ?? 'api_error',
				'message' => $data['message'] ?? __('Failed to remove home.', 'form-connector'),
			];
		}

		return [
			'success' => true,
			'message' => __('Home removed successfully.', 'form-connector'),
			'data' => $data['data'] ?? null,
		];
	}

	/**
	 * Add a new home
	 *
	 * @param array $home Home data with homeId, name, and optional provider
	 */
	public function add_home(array $home): array {
		if (empty($home['homeId']) || empty($home['name'])) {
			return [
				'success' => false,
				'error' => 'invalid_home',
				'message' => __('Home ID and name are required.', 'form-connector'),
			];
		}

		if (!$this->license->is_active()) {
			return [
				'success' => false,
				'error' => 'license_inactive',
				'message' => __('License must be active to add homes.', 'form-connector'),
			];
		}

		$key = $this->license->get_key();
		if (empty($key)) {
			return [
				'success' => false,
				'error' => 'no_license_key',
				'message' => __('No license key configured.', 'form-connector'),
			];
		}

		// Check if home already exists
		$existing = $this->get_homes(false);
		if ($existing['success'] && !empty($existing['data']['homes'])) {
			foreach ($existing['data']['homes'] as $existingHome) {
				if (($existingHome['homeId'] ?? '') === $home['homeId']) {
					return [
						'success' => false,
						'error' => 'home_exists',
						'message' => __('This home has already been added.', 'form-connector'),
					];
				}
			}
		}

		// Clear cache before operation
		delete_transient(self::TRANSIENT_KEY);

		// Send only the NEW home to the sync endpoint (not the entire list).
		// The API adds homes incrementally; it doesn't replace the whole list.
		$url = rtrim($this->api_endpoint, '/') . '/api/homes/sync';

		$body = wp_json_encode([
			'siteUrl' => home_url(),
			'homes'   => [
				[
					'homeId' => sanitize_text_field($home['homeId']),
					'name' => sanitize_text_field($home['name']),
					'provider' => sanitize_text_field($home['provider'] ?? 'CareHQ'),
				]
			],
		]);

		$response = wp_remote_post($url, [
			'timeout' => 30,
			'headers' => \FormConnector\Integration\CareHQ\Signing::build_signed_headers($body, $key),
			'body'    => $body,
		]);

		if (is_wp_error($response)) {
			return [
				'success' => false,
				'error' => 'network_error',
				'message' => $response->get_error_message(),
			];
		}

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

		// Log API response for debugging
		error_log(sprintf('[FormConnector] /api/homes/sync (add_home) response (HTTP %d): %s', $code, $body));

		if ($code === 403 && isset($data['error']) && $data['error'] === 'Insufficient seats') {
			return [
				'success' => false,
				'error' => 'insufficient_seats',
				'message' => $data['message'] ?? __('Not enough seats available.', 'form-connector'),
				'data' => $data['data'] ?? null,
			];
		}

		// Detect auth / API-key errors and invalidate license cache
		if ($this->is_auth_error($code, $data)) {
			return $this->auth_error_response($code, $data);
		}

		if ($code < 200 || $code >= 300) {
			return [
				'success' => false,
				'error' => $data['error'] ?? 'api_error',
				'message' => $data['message'] ?? __('Failed to add home.', 'form-connector'),
			];
		}

		// Check response body success flag (server may return 200 with success:false)
		if (isset($data['success']) && $data['success'] === false) {
			return [
				'success' => false,
				'error' => $data['error'] ?? 'api_error',
				'message' => $data['message'] ?? __('Failed to add home.', 'form-connector'),
			];
		}

		// Server may return success:true but with errors and created:0
		// This happens when the DB INSERT fails but the server still reports overall success
		$errors_count = $data['data']['errors'] ?? 0;
		$created_count = $data['data']['created'] ?? 0;
		if ($errors_count > 0 && $created_count === 0) {
			// Extract the first error detail for the user
			$error_details = $data['data']['details']['errors'] ?? [];
			$first_error = !empty($error_details) ? ($error_details[0]['error'] ?? '') : '';
			error_log(sprintf('[FormConnector] add_home reported success but home was not created. Errors: %s', wp_json_encode($error_details)));

			return [
				'success' => false,
				'error' => 'server_insert_failed',
				'message' => __('The server failed to save this home. Please try again or contact support.', 'form-connector'),
				'data' => $data['data'] ?? null,
			];
		}

		// Track last successful sync so future health pings can report it.
		update_option(\FormConnector\Integration\CareHQ\Signing::OPTION_LAST_SYNC, gmdate('c'), false);

		return [
			'success' => true,
			'message' => __('Home added successfully.', 'form-connector'),
			'data' => $data['data'] ?? null,
		];
	}

	/**
	 * Fetch homes from the API
	 */
	private function fetch_homes_from_api(): array {
		$key = $this->license->get_key();
		
		if (empty($key)) {
			return [
				'success' => false,
				'error' => 'no_license_key',
				'message' => __('No license key configured.', 'form-connector'),
			];
		}

		$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 [
				'success' => false,
				'error' => 'network_error',
				'message' => $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
		if ($code < 200 || $code >= 300 || (isset($data['success']) && !$data['success'])) {
			error_log(sprintf('[FormConnector] /api/homes response (HTTP %d): %s', $code, $body));
		}

		// Detect auth / API-key errors and invalidate license cache
		if ($this->is_auth_error($code, $data)) {
			return $this->auth_error_response($code, $data);
		}

		if ($code < 200 || $code >= 300) {
			return [
				'success' => false,
				'error' => $data['error'] ?? 'api_error',
				'message' => $data['message'] ?? __('Failed to fetch homes.', 'form-connector'),
			];
		}

		if (!isset($data['success']) || !$data['success']) {
			return [
				'success' => false,
				'error' => $data['error'] ?? 'api_error',
				'message' => $data['message'] ?? __('Failed to fetch homes.', 'form-connector'),
			];
		}

		$homes = $data['data']['homes'] ?? [];
		$subscription = self::normalize_subscription($data['data']['subscription'] ?? null, count($homes));

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

	/**
	 * Check if an HTTP response code + body indicates an API key auth failure.
	 * When detected, invalidates the local license cache so the UI reflects the real state.
	 *
	 * @param int        $code HTTP response code
	 * @param array|null $data Decoded JSON body (may be null)
	 * @return bool True if this is an auth/API-key error
	 */
	private function is_auth_error(int $code, ?array $data): bool {
		// Check HTTP status codes
		$is_auth_status = in_array($code, self::AUTH_ERROR_CODES, true);

		// Also check for auth-related error messages in the response body
		// (server may wrap the auth error in a 200 or 500 response)
		$auth_keywords = ['invalid api key', 'invalid_api_key', 'unauthorized', 'api key'];
		$error_text = strtolower($data['message'] ?? $data['error'] ?? '');
		$is_auth_message = false;
		foreach ($auth_keywords as $keyword) {
			if (str_contains($error_text, $keyword)) {
				$is_auth_message = true;
				break;
			}
		}

		if (!$is_auth_status && !$is_auth_message) {
			return false;
		}

		// Invalidate the license validation cache so the UI shows the real state
		$this->license->clear_validation_cache();

		// Also log it clearly
		$api_msg = $data['message'] ?? $data['error'] ?? 'Unknown auth error';
		error_log(sprintf(
			'[FormConnector] API key rejected by license server (HTTP %d): %s',
			$code,
			$api_msg
		));

		return true;
	}

	/**
	 * Build a standard auth-error return array.
	 *
	 * @param int        $code HTTP response code
	 * @param array|null $data Decoded JSON body
	 */
	private function auth_error_response(int $code, ?array $data): array {
		return [
			'success' => false,
			'license_error' => true,
			'error' => 'invalid_api_key',
			'message' => $data['message'] ?? $data['error'] ?? __('Invalid API key. Please check your license settings.', 'form-connector'),
		];
	}

	/**
	 * Get subscription status summary
	 */
	public function get_subscription_summary(): array {
		$result = $this->get_homes();
		
		if (!$result['success']) {
			return [
				'is_active' => false,
				'total_seats' => 0,
				'used_seats' => 0,
				'available_seats' => 0,
				'plan' => null,
			];
		}

		$homes = $result['data']['homes'] ?? [];
		$subscription = self::normalize_subscription($result['data']['subscription'] ?? [], count($homes)) ?? [];

		return [
			'is_active' => ($subscription['status'] ?? '') === 'active',
			'total_seats' => $subscription['totalSeats'] ?? null,
			'used_seats' => $subscription['usedSeats'] ?? count($homes),
			'available_seats' => $subscription['availableSeats'] ?? null,
			'is_unlimited' => (bool) ($subscription['isUnlimited'] ?? false),
			'plan' => $subscription['plan'] ?? null,
		];
	}

	/**
	 * Get the list of home IDs that are active (within the paid seat limit).
	 *
	 * When a user transitions from trial (unlimited) to paid, they may have
	 * more homes than their paid seats allow. Only the first N homes
	 * (respecting user priority order) are considered active for submissions.
	 *
	 * @return array{active_home_ids: string[], is_over_limit: bool, total_seats: ?int, used_seats: int, over_limit_count: int, is_unlimited: bool}
	 */
	public function get_active_home_ids(): array {
		$result = $this->get_homes(false);

		if (!$result['success']) {
			return [
				'active_home_ids' => [],
				'is_over_limit' => false,
				'total_seats' => null,
				'used_seats' => 0,
				'over_limit_count' => 0,
				'is_unlimited' => false,
			];
		}

		$homes = $result['data']['homes'] ?? [];
		$subscription = self::normalize_subscription($result['data']['subscription'] ?? [], count($homes)) ?? [];
		$total_seats = self::get_seat_limit($subscription);
		$used_seats = count($homes);
		$is_over_limit = $total_seats !== null && $total_seats > 0 && $used_seats > $total_seats;

		// Get home IDs in user-defined priority order
		$ordered_home_ids = $this->get_ordered_home_ids($homes);

		// Active homes = first N where N = totalSeats
		$active_home_ids = $total_seats !== null && $total_seats > 0
			? array_slice($ordered_home_ids, 0, $total_seats)
			: $ordered_home_ids; // If unlimited or missing, treat all as active (fail open)

		return [
			'active_home_ids' => $active_home_ids,
			'is_over_limit' => $is_over_limit,
			'total_seats' => $total_seats,
			'used_seats' => $used_seats,
			'over_limit_count' => $is_over_limit ? $used_seats - (int) $total_seats : 0,
			'is_unlimited' => (bool) ($subscription['isUnlimited'] ?? false),
		];
	}

	/**
	 * Check if a specific home is within the paid seat limit.
	 *
	 * Homes beyond the seat limit are inactive for submissions.
	 *
	 * @param string $home_id The CareHQ home/location ID
	 * @return bool True if home is within seat limit and can receive submissions
	 */
	public function is_home_within_seat_limit(string $home_id): bool {
		$active = $this->get_active_home_ids();
		return in_array($home_id, $active['active_home_ids'], true);
	}

	/**
	 * Get home IDs ordered by user-defined priority.
	 *
	 * Falls back to API order for homes not in the priority list.
	 *
	 * @param array $homes Array of home objects from the API
	 * @return string[] Ordered home IDs
	 */
	private function get_ordered_home_ids(array $homes): array {
		$all_home_ids = array_column($homes, 'homeId');
		$priority = get_option(self::OPTION_PRIORITY, []);

		if (empty($priority) || !is_array($priority)) {
			return $all_home_ids;
		}

		// Filter priority list to only include currently existing homes
		$ordered = array_values(array_intersect($priority, $all_home_ids));

		// Add any homes not in the priority list at the end
		$remaining = array_diff($all_home_ids, $ordered);

		return array_merge($ordered, array_values($remaining));
	}

	/**
	 * Save the user-defined priority order for homes.
	 *
	 * This determines which homes stay active when over the seat limit.
	 *
	 * @param string[] $ordered_home_ids Array of home IDs in priority order
	 * @return array{success: bool, message: string}
	 */
	public function save_home_priority(array $ordered_home_ids): array {
		// Sanitize
		$sanitized = array_map('sanitize_text_field', $ordered_home_ids);
		$sanitized = array_values(array_filter($sanitized));

		if (empty($sanitized)) {
			return [
				'success' => false,
				'message' => __('No valid home IDs provided.', 'form-connector'),
			];
		}

		update_option(self::OPTION_PRIORITY, $sanitized);

		// Clear the homes cache so active IDs recalculate
		delete_transient(self::TRANSIENT_KEY);

		return [
			'success' => true,
			'message' => __('Home priority updated.', 'form-connector'),
		];
	}

	/**
	 * Get the saved priority order.
	 *
	 * @return string[]
	 */
	public function get_home_priority(): array {
		$priority = get_option(self::OPTION_PRIORITY, []);
		return is_array($priority) ? $priority : [];
	}
}
