<?php
/**
 * SummPress Core Processing Logic
 *
 * Handles article extraction, summarization, and publication.
 *
 * @package SummPress
 * @since 1.0.0
 */

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

use fivefilters\Readability\Readability;
use fivefilters\Readability\Configuration;
use fivefilters\Readability\ParseException;

/**
 * Main SummPress Class
 *
 * Handles article processing, REST API, and post creation.
 *
 * @since 1.0.0
 */
class SummPress {

	/**
	 * The single instance of the class.
	 *
	 * @since 1.0.0
	 * @var SummPress
	 */
	protected static $instance = null;

	/**
	 * AI service instance.
	 *
	 * @since 1.1.0
	 * @var SummPress_AI_Service
	 */
	protected $ai_service;

	/**
	 * Main SummPress Instance.
	 *
	 * @since 1.0.0
	 * @return SummPress
	 */
	public static function instance() {
		if ( is_null( self::$instance ) ) {
			self::$instance = new self();
		}
		return self::$instance;
	}

	/**
	 * Constructor.
	 *
	 * @since 1.0.0
	 */
	public function __construct() {
		$this->load_ai_service();
		add_action( 'rest_api_init', [ $this, 'register_rest_routes' ] );
	}

	/**
	 * Load the AI service based on settings.
	 *
	 * @since 1.1.0
	 */
	protected function load_ai_service() {
		// 1. Base list of services (can be extended via the `summpress_ai_services` filter).
		$services = apply_filters( 'summpress_ai_services', [
			'gemini' => [
				'file'  => SUMMPRESS_PLUGIN_DIR . 'includes/ai-services/class-gemini-ai.php',
				'class' => 'SummPress_Gemini_AI',
			],
			'openai' => [
				'file'  => SUMMPRESS_PLUGIN_DIR . 'includes/ai-services/class-openai-ai.php',
				'class' => 'SummPress_OpenAI_AI',
			],
		] );

		// 2. Move the default service to the front of the array (highest priority).
		$default = $this->get_option( 'default_ai_service', 'gemini' );

		if ( isset( $services[ $default ] ) ) {
			$services = [ $default => $services[ $default ] ] + $services;
		}

		// 3. Iterate over services and pick the first one we can instantiate.
		$this->ai_service = null;
		foreach ( $services as $cfg ) {
			// Load the file if it exists and the class isn’t already available.
			if ( ! class_exists( $cfg['class'] )
			     && ! empty( $cfg['file'] )
			     && is_readable( $cfg['file'] ) ) {
				require_once $cfg['file'];
			}

			// Instantiate the class with $this (SummPress instance) if it now exists, then stop searching.
			if ( class_exists( $cfg['class'] ) ) {
				$this->ai_service = new $cfg['class']( $this ); // Передаємо $this
				break;
			}
		}

		// 4. Fallback: a minimal “null” provider that always returns WP_Error.
		if ( ! $this->ai_service ) {
			$this->ai_service = new class implements SummPress_AI_Service {
				public function generate_summary( string $title, string $content ) {
					return new \WP_Error( 'no_ai_service', __( 'No AI service available.', 'summpressai' ) );
				}
			};
		}

		// 5. Final chance for external code to override the service instance.
		$this->ai_service = apply_filters( 'summpress_ai_service', $this->ai_service );
	}

	/**
	 * Register REST API routes.
	 *
	 * @since 1.0.0
	 */
	public function register_rest_routes() {
		register_rest_route(
			'summpress-ai/v1',
			'/summarize',
			[
				'methods'             => WP_REST_Server::CREATABLE,
				'callback'            => [ $this, 'process_article_endpoint' ],
				'permission_callback' => [ $this, 'check_rest_permissions' ],
				'args'                => [
					'html' => [
						'required'    => true,
						'type'        => 'string',
						'description' => __( 'HTML content of the article to summarize', 'summpressai' ),
					],
				],
			]
		);
	}

	/**
	 * Check REST API permissions.
	 *
	 * @since 1.0.0
	 * @param WP_REST_Request $request The REST request.
	 * @return bool|WP_Error
	 */
	public function check_rest_permissions( $request ) {
		$headers = $request->get_headers();
		$auth_header = isset( $headers['authorization'][0] ) ? $headers['authorization'][0] : '';

		if ( strpos( $auth_header, 'Bearer ' ) !== 0 ) {
			return new WP_Error(
				'unauthorized',
				__( 'Missing or invalid Authorization header.', 'summpressai' ),
				[ 'status' => 401 ]
			);
		}

		$provided_token = trim( str_replace( 'Bearer', '', $auth_header ) );
		$saved_token = $this->get_option( 'extension_token' );

		if ( empty( $saved_token ) || $provided_token !== $saved_token ) {
			return new WP_Error(
				'invalid_token',
				__( 'Invalid or expired token.', 'summpressai' ),
				[ 'status' => 403 ]
			);
		}
		return true;
	}

	/**
	 * Process article REST API endpoint.
	 *
	 * @since 1.0.0
	 * @param WP_REST_Request $request The REST request.
	 * @return WP_REST_Response|WP_Error
	 */
	public function process_article_endpoint( $request ) {
		$html = $request->get_param( 'html' );

		if ( empty( $html ) ) {
			return new WP_Error(
				'missing_html',
				__( 'HTML content is required', 'summpressai' ),
				[ 'status' => 400 ]
			);
		}

		$result = $this->process_article( $html );

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

		return rest_ensure_response( $result );
	}

	/**
	 * Process an article from HTML content.
	 *
	 * @since 1.0.0
	 * @param string $html The HTML content.
	 * @return array|WP_Error
	 */
	public function process_article( $html ) {
		if ( empty( $html ) ) {
			return new WP_Error(
				'empty_content',
				__( 'No HTML content provided', 'summpressai' )
			);
		}

		try {
			$readability_config = new Configuration();
			$readability = new Readability( $readability_config );

			$readability->parse( $html );
			$title = $readability->getTitle() ?? __( 'Generate title based on content', 'summpressai' );
			$content = wp_strip_all_tags( $readability->getContent() );

			if ( empty( $content ) ) {
				return new WP_Error(
					'extraction_failed',
					__( 'Failed to extract content from the provided HTML', 'summpressai' )
				);
			}

			$summary = $this->ai_service->generate_summary( $title, $content );

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

			if ( empty( $summary['title'] ) || empty( $summary['text'] ) ) {
				return new WP_Error(
					'summary_generation_failed',
					__( 'Failed to generate summary from the content', 'summpressai' )
				);
			}

			$post_id = $this->create_summary_post( $summary );

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

			return [
				'success'       => true,
				'post_id'       => $post_id,
				'post_url'      => get_permalink( $post_id ),
				'post_edit_url' => admin_url( 'post.php?post=' . $post_id . '&action=edit' ),
			];
		} catch ( ParseException $e ) {
			return new WP_Error(
				'parse_error',
				sprintf(
				/* translators: %s: Error message */
					__( 'Error parsing HTML content: %s', 'summpressai' ),
					$e->getMessage()
				)
			);
		} catch ( Exception $e ) {
			return new WP_Error(
				'processing_error',
				sprintf(
				/* translators: %s: Error message */
					__( 'Error processing article: %s', 'summpressai' ),
					$e->getMessage()
				)
			);
		}
	}

	/**
	 * Create a WordPress post from summary data.
	 *
	 * @since 1.0.0
	 * @param array $summary Summary data with 'title' and 'text'.
	 * @return int|WP_Error
	 */
	protected function create_summary_post( $summary ) {
		if ( empty( $summary['title'] ) || empty( $summary['text'] ) ) {
			return new WP_Error(
				'invalid_summary',
				__( 'Summary is missing required data', 'summpressai' )
			);
		}

		$title = sanitize_text_field( $summary['title'] );
		$content = wp_kses_post( $summary['text'] );
		$slug = $this->generate_post_slug($title);

		$post_type = $this->get_option( 'post_type', 'post');
		$post_status = $this->get_option( 'post_status', 'draft' );


		$post_data = [
			'post_title'   => $title,
			'post_content' => $content,
			'post_status'  => $post_status,
			'post_type'    => $post_type,
			'post_name'    => $slug,
			'meta_input'   => [
				'_summpress_generated' => true,
				'_summpress_version'   => SUMMPRESS_VERSION,
			],
		];

		/**
		 * Filter the post data before insertion.
		 *
		 * @since 1.0.0
		 * @param array $post_data Post data.
		 * @param array $summary Summary data.
		 */
		$post_data = apply_filters( 'summpress_post_data', $post_data, $summary );

		$post_id = wp_insert_post( $post_data, true );

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

		/**
		 * Fires after a summary post is created.
		 *
		 * @since 1.0.0
		 * @param int $post_id Post ID.
		 * @param array $summary Summary data.
		 */
		do_action( 'summpress_post_created', $post_id, $summary );

		return $post_id;
	}

	/**
	 * Generate a slug for the post from title.
	 *
	 * @since 1.0.0
	 * @param string $title Post title.
	 * @return string Sanitized slug.
	 */
	protected function generate_post_slug( $title ) {
		if ( $this->contains_non_latin( $title ) ) {
			$slug = $this->transliterate_text( $title );
		} else {
			$slug = sanitize_title( $title );
		}

		return $slug;
	}

	/**
	 * Check if a string contains non-Latin characters.
	 *
	 * @since 1.0.0
	 * @param string $text Text to check.
	 * @return bool
	 */
	protected function contains_non_latin( $text ) {
		return (bool) preg_match( '/[^\x20-\x7E]/', $text );
	}

	/**
	 * Transliterate non-Latin characters to Latin.
	 *
	 * @since 1.0.0
	 * @param string $text Text to transliterate.
	 * @return string Transliterated text.
	 */
	public function transliterate_text( $text ) {
		// Transliteration map for Cyrillic and other characters.
		$char_map = array(
			// Lowercase Cyrillic letters
			'а' => 'a', 'б' => 'b', 'в' => 'v', 'г' => 'h', 'ґ' => 'g',
			'д' => 'd', 'е' => 'e', 'є' => 'ye', 'ж' => 'zh', 'з' => 'z',
			'и' => 'y', 'і' => 'i', 'ї' => 'yi', 'й' => 'y', 'к' => 'k',
			'л' => 'l', 'м' => 'm', 'н' => 'n', 'о' => 'o', 'п' => 'p',
			'р' => 'r', 'с' => 's', 'т' => 't', 'у' => 'u', 'ф' => 'f',
			'х' => 'kh', 'ц' => 'ts', 'ч' => 'ch', 'ш' => 'sh', 'щ' => 'shch',
			'ь' => '', 'ю' => 'yu', 'я' => 'ya', 'ъ' => '', 'ы' => 'y',
			'э' => 'e',

			// Uppercase Cyrillic letters
			'А' => 'a', 'Б' => 'b', 'В' => 'v', 'Г' => 'h', 'Ґ' => 'g',
			'Д' => 'd', 'Е' => 'e', 'Є' => 'ye', 'Ж' => 'zh', 'З' => 'z',
			'И' => 'y', 'І' => 'i', 'Ї' => 'yi', 'Й' => 'y', 'К' => 'k',
			'Л' => 'l', 'М' => 'm', 'Н' => 'n', 'О' => 'o', 'П' => 'p',
			'Р' => 'r', 'С' => 's', 'Т' => 't', 'У' => 'u', 'Ф' => 'f',
			'Х' => 'kh', 'Ц' => 'ts', 'Ч' => 'ch', 'Ш' => 'sh', 'Щ' => 'shch',
			'Ь' => '', 'Ю' => 'yu', 'Я' => 'ya', 'Ъ' => '', 'Ы' => 'y',
			'Э' => 'e',

			// Common European special characters
			'à' => 'a', 'á' => 'a', 'â' => 'a', 'ã' => 'a', 'ä' => 'a', 'å' => 'a',
			'æ' => 'ae', 'ç' => 'c', 'è' => 'e', 'é' => 'e', 'ê' => 'e', 'ë' => 'e',
			'ì' => 'i', 'í' => 'i', 'î' => 'i', 'ï' => 'i', 'ð' => 'd', 'ñ' => 'n',
			'ò' => 'o', 'ó' => 'o', 'ô' => 'o', 'õ' => 'o', 'ö' => 'o', 'ø' => 'o',
			'ù' => 'u', 'ú' => 'u', 'û' => 'u', 'ü' => 'u', 'ý' => 'y', 'ÿ' => 'y',

			// Uppercase European special characters
			'À' => 'a', 'Á' => 'a', 'Â' => 'a', 'Ã' => 'a', 'Ä' => 'a', 'Å' => 'a',
			'Æ' => 'ae', 'Ç' => 'c', 'È' => 'e', 'É' => 'e', 'Ê' => 'e', 'Ë' => 'e',
			'Ì' => 'i', 'Í' => 'i', 'Î' => 'i', 'Ï' => 'i', 'Ð' => 'd', 'Ñ' => 'n',
			'Ò' => 'o', 'Ó' => 'o', 'Ô' => 'o', 'Õ' => 'o', 'Ö' => 'o', 'Ø' => 'o',
			'Ù' => 'u', 'Ú' => 'u', 'Û' => 'u', 'Ü' => 'u', 'Ý' => 'y',
		);

		// Apply transliteration mappings
		$text = strtr( $text, $char_map );

		// Convert to lowercase
		$text = strtolower( $text );

		// Replace any remaining non-alphanumeric characters with hyphens
		$text = preg_replace( '/[^a-z0-9\-]/', '-', $text );

		// Replace multiple hyphens with a single hyphen
		$text = preg_replace( '/-+/', '-', $text );

		// Remove leading and trailing hyphens
		$text = trim( $text, '-' );

		return $text;
    }

    /**
     * Get plugin option with default fallback.
     *
     * @since 1.0.0
     * @param string $key Option key.
     * @param mixed  $default Default value.
     * @return mixed
     */
	public function get_option( $key, $default = '' ) {
		$options = get_option( 'summpress_settings', [] );
		return isset( $options[ $key ] ) ? $options[ $key ] : $default;
	}
}

/**
 * Returns the main SummPress instance.
 *
 * @since 1.0.0
 * @return SummPress
 */
function SummPress() {
    return SummPress::instance();
}

// Initialize the plugin.
SummPress();
