<?php
/**
 * Preview Cache Service
 *
 * @category   WordPress_Plugin
 * @package    Common
 * @subpackage Services\Preview
 * @author     miniOrange <info@miniorange.com>
 * @license    MIT/Expat
 * @link       https://plugins.miniorange.com/woocommerce-api-product-sync-with-woocommerce-rest-apis
 */

namespace MOWCPS\Common\Services\Preview;

use WP_Error;
use MOWCPS\Common\Constants\Constants;
use MOWCPS\Common\Services\Logging\Supplier_Logger;

if ( ! defined( 'ABSPATH' ) ) {
	die;
}

/**
 * Class Preview_Cache_Service
 *
 * Handles caching of preview product data in files
 */
class Preview_Cache_Service {

	/**
	 * Cache directory path
	 *
	 * @var string
	 */
	private $cache_dir;

	/**
	 * Supplier ID
	 *
	 * @var int
	 */
	private $supplier_id;

	/**
	 * Constructor
	 *
	 * @param int $supplier_id Supplier ID.
	 */
	public function __construct( $supplier_id ) {
		$this->supplier_id = $supplier_id;
		$this->cache_dir   = trailingslashit( MOWCPS_PLUGIN_DIR ) . 'data' . DIRECTORY_SEPARATOR . 'preview-cache';
		$this->ensure_cache_directory();
	}

	/**
	 * Ensure cache directory exists
	 *
	 * @return void
	 */
	private function ensure_cache_directory() {
		if ( ! file_exists( $this->cache_dir ) ) {
			wp_mkdir_p( $this->cache_dir );
		}
	}

	/**
	 * Get cache file path for supplier
	 *
	 * @return string Cache file path
	 */
	private function get_cache_file_path() {
		return $this->cache_dir . DIRECTORY_SEPARATOR . 'preview_' . $this->supplier_id . '.json';
	}

	/**
	 * Save products to cache file
	 *
	 * @param array $products Array of products.
	 * @param array $metadata Additional metadata (pagination, columns, etc.).
	 * @return bool|WP_Error True on success, WP_Error on failure
	 */
	public function save_to_cache( $products, $metadata = array() ) {
		$cache_file = $this->get_cache_file_path();

		$cache_data = array(
			'products'   => $products,
			'metadata'   => $metadata,
			'created_at' => current_time( 'mysql' ),
			'timestamp'  => time(),
		);

		$json_data = wp_json_encode( $cache_data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES );

		if ( false === $json_data ) {
			Supplier_Logger::debug( $this->supplier_id, 'ERROR: Failed to encode cache data to JSON' );
			return new WP_Error(
				Constants::ERROR_GENERAL,
				'Failed to encode cache data',
				array( 'status' => Constants::HTTP_INTERNAL_SERVER_ERROR )
			);
		}

		$result = file_put_contents( $cache_file, $json_data );

		if ( false === $result ) {
			Supplier_Logger::debug( $this->supplier_id, 'ERROR: Failed to write cache file: ' . $cache_file );
			return new WP_Error(
				Constants::ERROR_GENERAL,
				'Failed to write cache file',
				array( 'status' => Constants::HTTP_INTERNAL_SERVER_ERROR )
			);
		}

		Supplier_Logger::debug( $this->supplier_id, 'Saved ' . count( $products ) . ' products to cache file' );

		return true;
	}

	/**
	 * Load products from cache file
	 *
	 * @return array|WP_Error Cache data or error
	 */
	public function load_from_cache() {
		$cache_file = $this->get_cache_file_path();

		if ( ! file_exists( $cache_file ) ) {
			return new WP_Error(
				Constants::ERROR_RECORD_NOT_FOUND,
				'Cache file not found',
				array( 'status' => Constants::HTTP_NOT_FOUND )
			);
		}

		@ini_set( 'memory_limit', '-1' );
		$json_data = file_get_contents( $cache_file );

		if ( false === $json_data ) {
			Supplier_Logger::debug( $this->supplier_id, 'ERROR: Failed to read cache file: ' . $cache_file );
			return new WP_Error(
				Constants::ERROR_GENERAL,
				'Failed to read cache file',
				array( 'status' => Constants::HTTP_INTERNAL_SERVER_ERROR )
			);
		}

		@ini_set( 'memory_limit', '2G' );
		$cache_data = json_decode( $json_data, true );

		if ( null === $cache_data ) {
			Supplier_Logger::debug( $this->supplier_id, 'ERROR: Failed to decode cache file JSON' );
			return new WP_Error(
				Constants::ERROR_GENERAL,
				'Failed to decode cache file',
				array( 'status' => Constants::HTTP_INTERNAL_SERVER_ERROR )
			);
		}

		Supplier_Logger::debug( $this->supplier_id, 'Loaded cache file with ' . count( $cache_data['products'] ) . ' products' );

		return $cache_data;
	}

	/**
	 * Get cache metadata only (without products)
	 *
	 * @return array|WP_Error Metadata or error
	 */
	public function get_cache_metadata() {
		$cache_data = $this->load_from_cache();

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

		return array(
			'api_page'        => $cache_data['metadata']['api_page'] ?? 1,
			'pagination_info' => $cache_data['metadata']['pagination_info'] ?? array(),
			'columns'         => $cache_data['metadata']['columns'] ?? array(),
			'total_products'  => count( $cache_data['products'] ?? array() ),
		);
	}

	/**
	 * Append new products to existing cache
	 *
	 * @param array $new_products New products to append.
	 * @param array $pagination_info Pagination info for the new API page.
	 * @param int   $api_page Current API page number.
	 * @return bool|WP_Error True on success, WP_Error on failure
	 */
	public function append_to_cache( $new_products, $pagination_info, $api_page ) {
		$cache_data = $this->load_from_cache();

		if ( is_wp_error( $cache_data ) ) {
			$metadata = array(
				'api_page'        => $api_page,
				'pagination_info' => $pagination_info,
			);
			return $this->save_to_cache( $new_products, $metadata );
		}

		$existing_products = $cache_data['products'] ?? array();
		$merged_products   = array_merge( $existing_products, $new_products );

		$metadata = array(
			'api_page'        => $api_page,
			'pagination_info' => $pagination_info,
			'columns'         => $cache_data['metadata']['columns'] ?? array(),
		);

		// Save merged data.
		return $this->save_to_cache( $merged_products, $metadata );
	}

	/**
	 * Check if cache file exists and is not expired
	 *
	 * @param int $max_age_seconds Maximum age in seconds (default 3600 = 1 hour).
	 * @return bool True if cache is valid, false otherwise
	 */
	public function is_cache_valid( $max_age_seconds = 3600 ) {
		$cache_file = $this->get_cache_file_path();

		if ( ! file_exists( $cache_file ) ) {
			return false;
		}

		$file_time    = filemtime( $cache_file );
		$current_time = time();

		return ( $current_time - $file_time ) < $max_age_seconds;
	}

	/**
	 * Delete cache file
	 *
	 * @return bool True on success, false on failure
	 */
	public function delete_cache() {
		$cache_file = $this->get_cache_file_path();

		if ( ! file_exists( $cache_file ) ) {
			Supplier_Logger::debug( $this->supplier_id, 'Cache file already deleted or never existed' );
		} else {
			$result = wp_delete_file( $cache_file );

			if ( $result ) {
				Supplier_Logger::debug( $this->supplier_id, 'Deleted cache file' );
			} else {
				Supplier_Logger::debug( $this->supplier_id, 'ERROR: Failed to delete cache file: ' . $cache_file );
			}
		}
		return true;
	}

	/**
	 * Get paginated products from cache
	 *
	 * @param int $page Page number (1-based).
	 * @param int $per_page Items per page.
	 * @return array|WP_Error Paginated products or error
	 */
	public function get_paginated_from_cache( $page = 1, $per_page = 25 ) {
		$cache_data = $this->load_from_cache();

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

		$products       = $cache_data['products'];
		$total_products = count( $products );
		$total_pages    = ceil( $total_products / $per_page );

		if ( $page > $total_pages ) {
			Supplier_Logger::debug(
				$this->supplier_id,
				"Requested page {$page} exceeds cached pages {$total_pages}, need to fetch more from API"
			);
			return new WP_Error(
				'cache_exhausted',
				'Requested page exceeds cached data',
				array(
					'requested_page' => $page,
					'cached_pages'   => $total_pages,
					'total_products' => $total_products,
				)
			);
		}

		$page = max( 1, $page );

		$offset             = ( $page - 1 ) * $per_page;
		$paginated_products = array_slice( $products, $offset, $per_page );

		Supplier_Logger::debug(
			$this->supplier_id,
			"Returning page {$page} from cache (products {$offset}-" . ( $offset + count( $paginated_products ) ) . " of {$total_products})"
		);

		$more_in_cache   = ( $page < $total_pages );
		$source_has_more = ! empty( $cache_data['metadata']['pagination_info']['has_pagination'] );
		$has_more        = $more_in_cache || $source_has_more;

		Supplier_Logger::debug(
			$this->supplier_id,
			"Pagination check: page={$page}, total_pages={$total_pages}, more_in_cache={$more_in_cache}, source_has_more={$source_has_more}, has_more={$has_more}"
		);

		return array(
			'products'   => $paginated_products,
			'pagination' => array(
				'current_page'   => $page,
				'per_page'       => $per_page,
				'total_products' => $total_products,
				'total_pages'    => $total_pages,
				'has_more'       => $has_more,
			),
			'metadata'   => $cache_data['metadata'],
		);
	}
}
