<?php
/**
 * This file is part of the Wisewand WordPress plugin.
 *
 * Project Brief API Client - Handles fetching and caching of Wisewand project brief configuration.
 *
 * @package Wisewand
 * @since 2.8.0
 * @license GPL-2.0-or-later
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <https://www.gnu.org/licenses/>.
 */
namespace Wisewand\API;

// Exit if accessed directly
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

use Wisewand\Utils\Options;
use Wisewand\Logging\Logger;

// phpcs:disable WordPress.PHP.DevelopmentFunctions.error_log_error_log -- error_log() is only used within WP_DEBUG conditionals for debugging

/**
 * Project Brief API Client
 *
 * Manages fetching and caching of Wisewand project brief configuration.
 * Implements multi-tier caching strategy with graceful degradation.
 *
 * Cache Hierarchy:
 * 1. Object Cache (wp_cache) - 5 minutes
 * 2. Transient (database) - 1 hour
 * 3. Persistent Fallback (wp_options) - 24 hours
 * 4. API Fetch - Fresh from Wisewand API
 *
 * @package Wisewand
 * @since 2.8.0
 */
class Project_Brief_Client {

	/**
	 * Options manager
	 *
	 * @since 2.8.0
	 * @var Options
	 */
	private $options;

	/**
	 * Logger instance
	 *
	 * @since 2.8.0
	 * @var Logger
	 */
	private $logger;

	/**
	 * Wisewand API Client instance
	 *
	 * @since 2.8.0
	 * @var \Wisewand\Utils\Wisewand_API_Client
	 */
	private $api_client;

	/**
	 * Object cache group name
	 *
	 * @since 2.8.0
	 * @var string
	 */
	const CACHE_GROUP = 'wisewand';

	/**
	 * Object cache key
	 *
	 * @since 2.8.0
	 * @var string
	 */
	const CACHE_KEY = 'wisewand_project_brief';

	/**
	 * Transient cache duration (1 hour)
	 *
	 * @since 2.8.0
	 * @var int
	 */
	const TRANSIENT_DURATION = 3600;

	/**
	 * Object cache duration (5 minutes)
	 *
	 * @since 2.8.0
	 * @var int
	 */
	const OBJECT_CACHE_DURATION = 300;

	/**
	 * Persistent fallback duration (24 hours)
	 *
	 * @since 2.8.0
	 * @var int
	 */
	const FALLBACK_DURATION = 86400;

	/**
	 * Constructor
	 *
	 * @since 2.8.0
	 * @param Options $options Options manager instance.
	 * @param Logger  $logger  Logger instance.
	 */
	public function __construct( Options $options, Logger $logger ) {
		$this->options = $options;
		$this->logger  = $logger;

		// Initialize API client
		$api_key = $this->options->get( 'wisewand_api_key', '' );
		if ( ! empty( $api_key ) ) {
			$this->api_client = new \Wisewand\Utils\Wisewand_API_Client( $api_key, $this->logger );
		}
	}

	/**
	 * Get project brief configuration with multi-tier caching
	 *
	 * Implements cache hierarchy for performance and reliability:
	 * 1. Check object cache (5 minutes) - fastest
	 * 2. Check transient cache (1 hour) - fast
	 * 3. Check persistent fallback (24 hours) - reliable
	 * 4. Fetch from API - authoritative but slowest
	 *
	 * @since 2.8.0
	 * @param bool $force_refresh Force bypass all caches and fetch from API.
	 * @return array|WP_Error Project brief configuration or error.
	 */
	public function get_project_brief( $force_refresh = false ) {
		// TIER 1: Object Cache (fastest - 5 minutes)
		if ( ! $force_refresh ) {
			$brief = wp_cache_get( self::CACHE_KEY, self::CACHE_GROUP );
			if ( false !== $brief && ! empty( $brief ) ) {
				$this->logger->info(
					'Project brief retrieved from object cache',
					array( 'cache_tier' => 'memory' )
				);
				return $brief;
			}
		}

		// TIER 2: Transient Cache (fast - 1 hour)
		if ( ! $force_refresh ) {
			$brief = get_transient( self::CACHE_KEY );
			if ( false !== $brief && ! empty( $brief ) ) {
				// Repopulate object cache
				wp_cache_set( self::CACHE_KEY, $brief, self::CACHE_GROUP, self::OBJECT_CACHE_DURATION );

				$this->logger->info(
					'Project brief retrieved from transient cache',
					array( 'cache_tier' => 'transient' )
				);
				return $brief;
			}
		}

		// TIER 3: Fetch from API (authoritative)
		$brief = $this->fetch_from_api();

		if ( is_wp_error( $brief ) ) {
			// API fetch failed - attempt graceful degradation

			$this->logger->warning(
				'API brief fetch failed, attempting fallback',
				array(
					'error'   => $brief->get_error_message(),
					'code'    => $brief->get_error_code(),
					'context' => 'get_project_brief',
				)
			);

			// TIER 4: Persistent Fallback (24-hour stale cache)
			$fallback = get_option( 'wisewand_project_brief_fallback', false );

			if ( false !== $fallback && ! empty( $fallback ) ) {
				// Check if fallback is not too stale (max 7 days old)
				$fallback_age = isset( $fallback['cached_at'] ) ? ( time() - $fallback['cached_at'] ) : 0;

				if ( $fallback_age < ( 7 * DAY_IN_SECONDS ) ) {
					$this->logger->warning(
						'Using stale project brief fallback',
						array(
							'cache_age_hours' => round( $fallback_age / HOUR_IN_SECONDS, 1 ),
							'cache_tier'      => 'fallback',
						)
					);

					// Return stale data (better than failure)
					return $fallback['data'];
				}
			}

			// No fallback available - return error
			return $brief;
		}

		// API fetch successful - populate all cache tiers
		$this->cache_brief( $brief );

		$this->logger->info(
			'Project brief fetched from API and cached',
			array(
				'cache_tier' => 'api_fresh',
				'brief_keys' => array_keys( $brief ),
			)
		);

		return $brief;
	}

	/**
	 * Fetch project brief from Wisewand API
	 *
	 * @since 2.8.0
	 * @return array|WP_Error Project brief data or error.
	 */
	private function fetch_from_api() {
		if ( null === $this->api_client ) {
			return new \WP_Error(
				'api_client_not_initialized',
				__( 'Wisewand API client not initialized. Please configure your API key.', 'wisewand-ai-content-writer-seo-content-generator' )
			);
		}

		$project_id = $this->options->get( 'wisewand_default_project_id', '' );
		if ( empty( $project_id ) ) {
			return new \WP_Error(
				'missing_project_id',
				__( 'Project ID not configured. Please select a project in Settings.', 'wisewand-ai-content-writer-seo-content-generator' )
			);
		}

		// Fetch project brief from API
		try {
			$response = $this->api_client->get_project_brief( $project_id );

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

			// Validate response structure
			if ( ! is_array( $response ) ) {
				return new \WP_Error(
					'invalid_api_response',
					__( 'Invalid project brief response from API.', 'wisewand-ai-content-writer-seo-content-generator' )
				);
			}

			// Validate required fields exist
			$validation = $this->validate_brief_structure( $response );
			if ( is_wp_error( $validation ) ) {
				return $validation;
			}

			return $response;

		} catch ( \Exception $e ) {
			$this->logger->error(
				'Exception during project brief API fetch',
				array(
					'exception' => $e->getMessage(),
					'trace'     => $e->getTraceAsString(),
				)
			);

			return new \WP_Error(
				'api_exception',
				sprintf(
					/* translators: %s: Exception message */
					__( 'API error: %s', 'wisewand-ai-content-writer-seo-content-generator' ),
					$e->getMessage()
				)
			);
		}
	}

	/**
	 * Validate project brief structure
	 *
	 * Ensures brief contains required fields for content generation.
	 *
	 * @since 2.8.0
	 * @param array $brief Project brief data.
	 * @return true|WP_Error True if valid, WP_Error otherwise.
	 */
	private function validate_brief_structure( $brief ) {
		// Required fields for brief config to be considered "complete"
		// Adjust based on actual Wisewand API brief structure
		$required_fields = array(
			'is_brief_config_active', // Must be true
		);

		foreach ( $required_fields as $field ) {
			if ( ! isset( $brief[ $field ] ) ) {
				return new \WP_Error(
					'incomplete_brief',
					sprintf(
						/* translators: %s: Field name */
						__( 'Project brief missing required field: %s', 'wisewand-ai-content-writer-seo-content-generator' ),
						$field
					)
				);
			}
		}

		// Validate is_brief_config_active is true
		if ( empty( $brief['is_brief_config_active'] ) ) {
			return new \WP_Error(
				'brief_config_inactive',
				__( 'Project brief configuration is not active. Please activate it in your Wisewand project settings.', 'wisewand-ai-content-writer-seo-content-generator' )
			);
		}

		return true;
	}

	/**
	 * Cache project brief in all cache tiers
	 *
	 * @since 2.8.0
	 * @param array $brief Project brief data.
	 * @return void
	 */
	private function cache_brief( $brief ) {
		// TIER 1: Object Cache (5 minutes)
		wp_cache_set( self::CACHE_KEY, $brief, self::CACHE_GROUP, self::OBJECT_CACHE_DURATION );

		// TIER 2: Transient (1 hour)
		set_transient( self::CACHE_KEY, $brief, self::TRANSIENT_DURATION );

		// TIER 3: Persistent Fallback (24 hours with timestamp)
		$fallback_data = array(
			'data'      => $brief,
			'cached_at' => time(),
		);
		update_option( 'wisewand_project_brief_fallback', $fallback_data, false );
	}

	/**
	 * Invalidate all project brief caches
	 *
	 * Call this when project brief is updated in Wisewand UI.
	 *
	 * @since 2.8.0
	 * @return void
	 */
	public function invalidate_cache() {
		// Clear object cache
		wp_cache_delete( self::CACHE_KEY, self::CACHE_GROUP );

		// Clear transient cache
		delete_transient( self::CACHE_KEY );

		// Do NOT delete persistent fallback (needed for API failures)
		// It will be updated on next successful API fetch

		$this->logger->info(
			'Project brief cache invalidated',
			array( 'tiers_cleared' => 'object,transient' )
		);

		/**
		 * Fires after project brief cache is invalidated
		 *
		 * @since 2.8.0
		 */
		do_action( 'wisewand_project_brief_cache_invalidated' );
	}

	/**
	 * Warm up cache by fetching brief and populating all tiers
	 *
	 * Useful for pre-loading cache after settings changes.
	 *
	 * @since 2.8.0
	 * @return array|WP_Error Project brief or error.
	 */
	public function warm_cache() {
		// Invalidate existing caches
		$this->invalidate_cache();

		// Fetch fresh from API and populate caches
		return $this->get_project_brief( true );
	}

	/**
	 * Check if project brief is configured and valid
	 *
	 * Validates that:
	 * 1. Project ID is set
	 * 2. API key is configured
	 * 3. Brief can be fetched from API
	 * 4. Brief has is_brief_config_active = true
	 *
	 * @since 2.8.0
	 * @return true|WP_Error True if valid, WP_Error with details otherwise.
	 */
	public function validate_brief_configured() {
		// Check project ID
		$project_id = $this->options->get( 'wisewand_default_project_id', '' );
		if ( empty( $project_id ) ) {
			return new \WP_Error(
				'missing_project_id',
				__( 'Project ID not configured. Please select a project in Settings → API Keys.', 'wisewand-ai-content-writer-seo-content-generator' )
			);
		}

		// Check API key
		$api_key = $this->options->get( 'wisewand_api_key', '' );
		if ( empty( $api_key ) ) {
			return new \WP_Error(
				'missing_api_key',
				__( 'Wisewand API key not configured. Please add your API key in Settings.', 'wisewand-ai-content-writer-seo-content-generator' )
			);
		}

		// Fetch brief from API (force refresh to validate)
		$brief = $this->get_project_brief( true );

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

		// Validate structure
		$validation = $this->validate_brief_structure( $brief );
		if ( is_wp_error( $validation ) ) {
			return $validation;
		}

		// All checks passed
		return true;
	}

	/**
	 * Get cache statistics for debugging and monitoring
	 *
	 * @since 2.8.0
	 * @return array Cache statistics.
	 */
	public function get_cache_stats() {
		$stats = array(
			'object_cache_exists'   => false,
			'transient_cache_exists' => false,
			'fallback_cache_exists' => false,
			'fallback_age_hours'    => 0,
		);

		// Check object cache
		$object_cache = wp_cache_get( self::CACHE_KEY, self::CACHE_GROUP );
		$stats['object_cache_exists'] = ( false !== $object_cache );

		// Check transient
		$transient = get_transient( self::CACHE_KEY );
		$stats['transient_cache_exists'] = ( false !== $transient );

		// Check fallback
		$fallback = get_option( 'wisewand_project_brief_fallback', false );
		if ( false !== $fallback && isset( $fallback['cached_at'] ) ) {
			$stats['fallback_cache_exists'] = true;
			$stats['fallback_age_hours']    = round( ( time() - $fallback['cached_at'] ) / HOUR_IN_SECONDS, 1 );
		}

		return $stats;
	}

	/**
	 * Get cache tier used for last brief fetch
	 *
	 * Useful for monitoring cache performance.
	 *
	 * @since 2.8.0
	 * @return string Cache tier: 'memory', 'transient', 'fallback', 'api', or 'none'.
	 */
	public function get_last_cache_tier() {
		return get_transient( 'wisewand_brief_last_cache_tier' ) ?: 'none';
	}

	/**
	 * Clear all project brief caches (complete reset)
	 *
	 * WARNING: Use with caution. Forces all future requests to hit API.
	 *
	 * @since 2.8.0
	 * @param bool $include_fallback Whether to also delete persistent fallback.
	 * @return void
	 */
	public function clear_all_caches( $include_fallback = false ) {
		// Clear object cache
		wp_cache_delete( self::CACHE_KEY, self::CACHE_GROUP );

		// Clear transient
		delete_transient( self::CACHE_KEY );

		// Optionally clear persistent fallback (dangerous - removes safety net)
		if ( $include_fallback ) {
			delete_option( 'wisewand_project_brief_fallback' );

			$this->logger->warning(
				'ALL project brief caches cleared including fallback',
				array( 'tiers_cleared' => 'object,transient,fallback' )
			);
		} else {
			$this->logger->info(
				'Project brief caches cleared (fallback preserved)',
				array( 'tiers_cleared' => 'object,transient' )
			);
		}
	}
}
