<?php

declare(strict_types=1);

namespace FormConnector\Integration\CareHQ;

use FormConnector\Features\Logs\Logs;
use WP_Error;

/**
 * CareHQ API Client
 * 
 * Handles all communication with the CareHQ CRM API.
 * Provides proper error handling, logging, and retry logic.
 */
class APIClient {

	/**
	 * Singleton instance
	 */
	private static ?APIClient $instance = null;

	/**
	 * CareHQ SDK client instance
	 */
	private ?\CareHQ\APIClient $client = null;

	/**
	 * Settings instance
	 */
	private Settings $settings;

	/**
	 * Error log option name
	 */
	private const ERROR_LOG_OPTION = 'fc_carehq_error_log';

	/**
	 * Max errors to keep in log
	 */
	private const MAX_ERROR_LOG_ENTRIES = 50;

	/**
	 * Get singleton instance
	 */
	public static function get_instance(): self {
		if (self::$instance === null) {
			self::$instance = new self();
		}
		return self::$instance;
	}

	/**
	 * Constructor
	 */
	private function __construct() {
		$this->settings = new Settings();
	}

	/**
	 * Get or initialize the CareHQ API client
	 *
	 * @param bool $force_new Force creating a new client instance
	 * @return \CareHQ\APIClient|WP_Error
	 */
	public function get_client(bool $force_new = false) {
		if ($this->client !== null && !$force_new) {
			return $this->client;
		}

		// Check if SDK is available
		if (!class_exists('\CareHQ\APIClient')) {
			$error = new WP_Error(
				'sdk_missing',
				__('CareHQ SDK not found. Please ensure Composer dependencies are installed.', 'form-connector')
			);
			$this->log_error('SDK not found', ['error' => 'sdk_missing']);
			return $error;
		}

		// Get credentials
		$creds = $this->settings->get_credentials();

		// Validate credentials
		$missing = [];
		if (empty($creds['account_id'])) $missing[] = 'Account ID';
		if (empty($creds['api_key']))    $missing[] = 'API Key';
		if (empty($creds['api_secret'])) $missing[] = 'API Secret';

		if (!empty($missing)) {
			$error = new WP_Error(
				'missing_credentials',
				sprintf(
					__('CareHQ API credentials missing: %s', 'form-connector'),
					implode(', ', $missing)
				)
			);
			$this->log_error('Missing credentials', ['missing' => $missing]);
			return $error;
		}

		try {
			$this->client = new \CareHQ\APIClient(
				$creds['account_id'],
				$creds['api_key'],
				$creds['api_secret'],
				$creds['api_url'],
				$creds['timeout']
			);

			return $this->client;

		} catch (\Throwable $e) {
			$error = new WP_Error(
				'client_init_failed',
				__('Failed to initialize CareHQ client: ', 'form-connector') . $e->getMessage()
			);
			$this->log_error('Client initialization failed', [
				'message' => $e->getMessage(),
				'code' => $e->getCode(),
			]);
			return $error;
		}
	}

	/**
	 * Send a care enquiry to CareHQ
	 *
	 * @param array $data The enquiry data
	 * @return array|WP_Error API response or error
	 */
	public function send_care_enquiry(array $data): array|WP_Error {
		$client = $this->get_client();

		if (is_wp_error($client)) {
			return $client;
		}

		// Add required defaults if not present
		$data = $this->apply_defaults($data);

		// Validate minimum required fields
		$validation = $this->validate_enquiry_data($data);
		if (is_wp_error($validation)) {
			return $validation;
		}

		try {
			$response = $client->request('PUT', 'care-enquiries', null, $data);

			// Log successful submission to debug
			$this->log_debug('Care enquiry sent successfully', [
				'location' => $data['location'] ?? 'not-set',
				'response_id' => $response['_id'] ?? null,
			]);

			return $response;

		} catch (\CareHQ\Exception\APIException $e) {
			$error_info = $this->format_api_exception($e);
			
			$this->log_error('API Exception sending care enquiry', [
				'message' => $error_info['message'],
				'status_code' => $error_info['status_code'],
				'hint' => $error_info['hint'],
				'arg_errors' => $error_info['arg_errors'],
				'payload_keys' => array_keys($data),
			]);

			return new WP_Error(
				'api_error',
				$error_info['message'],
				$error_info
			);

		} catch (\Throwable $e) {
			$this->log_error('General error sending care enquiry', [
				'message' => $e->getMessage(),
				'code' => $e->getCode(),
				'class' => get_class($e),
			]);

			return new WP_Error(
				'request_failed',
				__('Request to CareHQ failed: ', 'form-connector') . $e->getMessage()
			);
		}
	}

	/**
	 * Apply default values to enquiry data
	 *
	 * @param array $data Enquiry data
	 * @return array Data with defaults applied
	 */
	private function apply_defaults(array $data): array {
		// Get service and funding_type defaults from settings
		$settings = $this->settings->get_settings();

		$defaults = [
			'funding_type' => $settings['funding_type'] ?? 'not_sure',
			'service' => $settings['service'] ?? 'residential_home',
			'first_name' => '-',
			'last_name' => '-',
		];

		foreach ($defaults as $key => $value) {
			if (empty($data[$key])) {
				$data[$key] = $value;
			}
		}

		// Apply untrusted_source flag (defaults to true for web form leads)
		if (!isset($data['untrusted_source'])) {
			$data['untrusted_source'] = $settings['untrusted_source'] ?? true;
		}

		return $data;
	}

	/**
	 * Validate enquiry data has minimum required fields
	 *
	 * @param array $data Enquiry data
	 * @return true|WP_Error True if valid, error otherwise
	 */
	private function validate_enquiry_data(array $data): bool|WP_Error {
		// Location is required
		if (empty($data['location'])) {
			return new WP_Error(
				'missing_location',
				__('Location (Home) is required to submit a care enquiry.', 'form-connector')
			);
		}

		// Must have at least some contact info
		$has_contact = !empty($data['email']) || !empty($data['phone']);
		$has_name = !empty($data['first_name']) && $data['first_name'] !== '-';
		
		if (!$has_contact && !$has_name) {
			return new WP_Error(
				'insufficient_data',
				__('Care enquiry must have at least a name, email, or phone number.', 'form-connector')
			);
		}

		return true;
	}

	/**
	 * Format CareHQ API exception into readable info
	 *
	 * @param \CareHQ\Exception\APIException $e The exception
	 * @return array Formatted error information
	 */
	private function format_api_exception(\CareHQ\Exception\APIException $e): array {
		$info = [
			'message' => '',
			'status_code' => $e->status_code ?? 'unknown',
			'hint' => $e->hint ?? null,
			'arg_errors' => $e->arg_errors ?? null,
			'doc_str' => $e->doc_str ?? null,
		];

		// Build readable message
		$parts = [];

		if (!empty($e->status_code)) {
			$parts[] = 'Status: ' . $e->status_code;
		}

		if (!empty($e->hint)) {
			$parts[] = $e->hint;
		}

		if (!empty($e->arg_errors)) {
			if (is_array($e->arg_errors)) {
				// arg_errors is typically ['field_name' => ['error1', 'error2']]
				$error_messages = [];
				foreach ($e->arg_errors as $field => $errors) {
					if (is_array($errors)) {
						$error_messages[] = $field . ': ' . implode(', ', $errors);
					} else {
						$error_messages[] = $field . ': ' . (string) $errors;
					}
				}
				$parts[] = 'Field errors: ' . implode('; ', $error_messages);
			} else {
				$parts[] = 'Error: ' . (string) $e->arg_errors;
			}
		}

		if (empty($parts)) {
			$str_value = (string) $e;
			if (!empty($str_value) && $str_value !== 'CareHQ\Exception\APIException') {
				$parts[] = $str_value;
			} else {
				$parts[] = 'Unknown API error';
			}
		}

		$info['message'] = implode(' - ', $parts);

		return $info;
	}

	/**
	 * Log debug message (only in WP_DEBUG mode)
	 *
	 * @param string $message Log message
	 * @param array $context Additional context
	 */
	private function log_debug(string $message, array $context = []): void {
		if (defined('WP_DEBUG') && WP_DEBUG) {
			error_log(sprintf(
				'[FormConnector API] %s | %s',
				$message,
				wp_json_encode($context)
			));
		}
	}

	/**
	 * Log error message
	 *
	 * @param string $message Error message
	 * @param array $context Additional context
	 */
	private function log_error(string $message, array $context = []): void {
		// Always log to error_log
		error_log(sprintf(
			'[FormConnector API ERROR] %s | %s',
			$message,
			wp_json_encode($context)
		));

		// Also store in options for admin display
		$this->store_error_log($message, $context);
	}

	/**
	 * Store error in persistent log
	 *
	 * @param string $message Error message
	 * @param array $context Additional context
	 */
	private function store_error_log(string $message, array $context = []): void {
		$log = get_option(self::ERROR_LOG_OPTION, []);
		
		if (!is_array($log)) {
			$log = [];
		}

		// Add new entry at the beginning
		array_unshift($log, [
			'timestamp' => current_time('mysql', true),
			'message' => $message,
			'context' => $context,
		]);

		// Keep only the latest entries
		$log = array_slice($log, 0, self::MAX_ERROR_LOG_ENTRIES);

		update_option(self::ERROR_LOG_OPTION, $log, false);
	}

	/**
	 * Get stored error log
	 *
	 * @return array Error log entries
	 */
	public function get_error_log(): array {
		$log = get_option(self::ERROR_LOG_OPTION, []);
		return is_array($log) ? $log : [];
	}

	/**
	 * Clear error log
	 */
	public function clear_error_log(): void {
		delete_option(self::ERROR_LOG_OPTION);
	}

	/**
	 * Get locations from CareHQ API
	 *
	 * Fetches up to 100 locations per request (CareHQ defaults to 10).
	 * Filtered to care-home service types only (excludes home_care,
	 * live_in_care, and independent_living).
	 *
	 * Available CareHQ service types for reference:
	 *   assisted_living, residential_home, nursing_home, independent_living,
	 *   learning_disability_care, bariatric_care, specialist_care,
	 *   home_care, live_in_care
	 *
	 * @see https://carehq.co.uk/docs — CareHQ API: GET /v1/locations
	 *
	 * @param bool $force_refresh Bypass cache
	 * @return array|WP_Error Locations or error
	 */
	public function get_locations(bool $force_refresh = false): array|WP_Error {
		$cache_key = 'fc_carehq_locations';
		$cache_duration = 3600; // 1 hour

		if (!$force_refresh) {
			$cached = get_transient($cache_key);
			if ($cached !== false) {
				return $cached;
			}
		}

		$client = $this->get_client();
		if (is_wp_error($client)) {
			return $client;
		}

		try {
			$result = $client->request('GET', 'locations', [
				'attributes' => ['_id', 'name'],
				'per_page' => 100,
				'filters-services' => [
					'residential_home',
					'nursing_home',
					'assisted_living',
					'specialist_care',
					'learning_disability_care',
					'bariatric_care',
				],
			]);

			$locations = $result['items'] ?? [];
			set_transient($cache_key, $locations, $cache_duration);

			return $locations;

		} catch (\Throwable $e) {
			$this->log_error('Failed to fetch locations', [
				'message' => $e->getMessage(),
			]);

			return new WP_Error(
				'locations_fetch_failed',
				__('Failed to retrieve locations: ', 'form-connector') . $e->getMessage()
			);
		}
	}

	/**
	 * Get sales channels from CareHQ API
	 *
	 * @param bool $force_refresh Bypass cache
	 * @return array|WP_Error Sales channels or error
	 */
	public function get_sales_channels(bool $force_refresh = false): array|WP_Error {
		$cache_key = 'fc_carehq_sales_channels';
		$cache_duration = 3600; // 1 hour

		if (!$force_refresh) {
			$cached = get_transient($cache_key);
			if ($cached !== false) {
				return $cached;
			}
		}

		$client = $this->get_client();
		if (is_wp_error($client)) {
			return $client;
		}

		try {
			$result = $client->request('GET', 'groups', [
				'attributes' => ['_id', 'name'],
				'filters-group_type' => 'sales_channel',
				'filters-archived' => 'no',
			]);

			$channels = $result['items'] ?? [];
			set_transient($cache_key, $channels, $cache_duration);

			return $channels;

		} catch (\Throwable $e) {
			$this->log_error('Failed to fetch sales channels', [
				'message' => $e->getMessage(),
			]);

			return new WP_Error(
				'sales_channels_fetch_failed',
				__('Failed to retrieve sales channels: ', 'form-connector') . $e->getMessage()
			);
		}
	}

	/**
	 * Clear locations cache
	 */
	public function clear_locations_cache(): void {
		delete_transient('fc_carehq_locations');
	}
}
