<?php
/**
 * Image ALT generation service.
 *
 * @package SmartyPress_AI_Engine
 */

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

/**
 * Handles attachment listing and AI metadata generation.
 */
class SmartyPress_AI_Image_ALT_Service {

	/**
	 * SEO context resolver.
	 *
	 * @var SmartyPress_AI_Image_ALT_SEO_Context
	 */
	private $seo_context;

	/**
	 * Prompt builder.
	 *
	 * @var SmartyPress_AI_Image_ALT_Prompt_Builder
	 */
	private $prompt_builder;

	/**
	 * Context analyzer.
	 *
	 * @var SmartyPress_AI_Image_ALT_Context_Analyzer
	 */
	private $context_analyzer;

	/**
	 * Constructor.
	 */
	public function __construct() {
		$this->seo_context      = new SmartyPress_AI_Image_ALT_SEO_Context();
		$this->prompt_builder   = new SmartyPress_AI_Image_ALT_Prompt_Builder();
		$this->context_analyzer = new SmartyPress_AI_Image_ALT_Context_Analyzer();
	}

	/**
	 * List image attachments for UI.
	 *
	 * @param array $args Query args.
	 * @return array
	 */
	public function get_attachments( array $args ) {
		$page     = max( 1, absint( $args['page'] ?? 1 ) );
		$per_page = min( 100, max( 10, absint( $args['per_page'] ?? 20 ) ) );
		$filter   = sanitize_key( $args['filter'] ?? 'all' );
		$search   = sanitize_text_field( $args['search'] ?? '' );

		$query_args = array(
			'post_type'      => 'attachment',
			'post_status'    => 'inherit',
			'post_mime_type' => 'image',
			'posts_per_page' => $per_page,
			'paged'          => $page,
			'orderby'        => 'date',
			'order'          => 'DESC',
		);

		if ( '' !== $search ) {
			$query_args['s'] = $search;
		}

		if ( 'missing_alt' === $filter ) {
			$query_args['meta_query'] = $this->missing_alt_meta_query(); // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query
		} elseif ( 'has_alt' === $filter ) {
			$query_args['meta_query'] = array( // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query
				array(
					'key'     => '_wp_attachment_image_alt',
					'value'   => '',
					'compare' => '!=',
				),
			);
		} elseif ( 'missing_caption' === $filter ) {
			$query_args['s'] = $search;
		}

		$query = new WP_Query( $query_args );
		$items = array();

		foreach ( $query->posts as $attachment ) {
			if ( 'missing_caption' === $filter && '' !== trim( (string) $attachment->post_excerpt ) ) {
				continue;
			}

			$context = $this->seo_context->resolve( $attachment->ID );
			$file    = get_attached_file( $attachment->ID );
			$alt     = get_post_meta( $attachment->ID, '_wp_attachment_image_alt', true );
			$error   = get_post_meta( $attachment->ID, 'smartypress_ai_alt_last_error', true );
			$status  = $this->get_attachment_status( $attachment->ID, $alt, $error );

			$items[] = array(
				'id'             => $attachment->ID,
				'title'          => $attachment->post_title,
				'alt'            => $alt,
				'caption'        => $attachment->post_excerpt,
				'description'    => $attachment->post_content,
				'url'            => wp_get_attachment_url( $attachment->ID ),
				'thumbnail_url'  => wp_get_attachment_image_url( $attachment->ID, 'thumbnail' ) ?: wp_get_attachment_url( $attachment->ID ),
				'filename'       => $file ? wp_basename( $file ) : '',
				'date'           => $attachment->post_date,
				'status'         => $status,
				'last_error'     => 'failed' === $status ? $error : '',
				'primary_post'   => $context['primary_post'],
				'focus_keywords' => $context['focus_keywords'],
				'strategy'       => get_post_meta( $attachment->ID, 'smartypress_ai_alt_strategy', true ),
				'confidence'     => get_post_meta( $attachment->ID, 'smartypress_ai_alt_confidence', true ),
				'needs_review'   => (bool) get_post_meta( $attachment->ID, 'smartypress_ai_alt_needs_review', true ),
			);
		}

		return array(
			'items'       => $items,
			'total'       => (int) $query->found_posts,
			'total_pages' => (int) $query->max_num_pages,
			'page'        => $page,
			'per_page'    => $per_page,
		);
	}

	/**
	 * Get media statistics.
	 *
	 * @return array
	 */
	public function get_stats() {
		global $wpdb;

		$total = (int) $wpdb->get_var( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
			$wpdb->prepare(
				'SELECT COUNT(*) FROM %i WHERE post_type = %s AND post_mime_type LIKE %s AND post_status = %s',
				$wpdb->posts,
				'attachment',
				'image/%',
				'inherit'
			)
		);

		$with_alt = (int) $wpdb->get_var( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
			$wpdb->prepare(
				"SELECT COUNT(DISTINCT p.ID)
				FROM %i p
				INNER JOIN %i pm ON p.ID = pm.post_id
				WHERE p.post_type = %s
				AND p.post_mime_type LIKE %s
				AND p.post_status = %s
				AND pm.meta_key = %s
				AND pm.meta_value != ''",
				$wpdb->posts,
				$wpdb->postmeta,
				'attachment',
				'image/%',
				'inherit',
				'_wp_attachment_image_alt'
			)
		);

		$generated = (int) $wpdb->get_var( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
			$wpdb->prepare(
				'SELECT COUNT(DISTINCT p.ID)
				FROM %i p
				INNER JOIN %i pm ON p.ID = pm.post_id
				WHERE p.post_type = %s
				AND p.post_mime_type LIKE %s
				AND p.post_status = %s
				AND pm.meta_key = %s',
				$wpdb->posts,
				$wpdb->postmeta,
				'attachment',
				'image/%',
				'inherit',
				'smartypress_ai_alt_generated'
			)
		);

		$failed = (int) $wpdb->get_var( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
			$wpdb->prepare(
				"SELECT COUNT(DISTINCT p.ID)
				FROM %i p
				INNER JOIN %i pm ON p.ID = pm.post_id
				WHERE p.post_type = %s
				AND p.post_mime_type LIKE %s
				AND p.post_status = %s
				AND pm.meta_key = %s
				AND pm.meta_value != ''
				AND NOT EXISTS (
					SELECT 1 FROM %i alt
					WHERE alt.post_id = p.ID
					AND alt.meta_key = %s
					AND alt.meta_value != ''
				)",
				$wpdb->posts,
				$wpdb->postmeta,
				'attachment',
				'image/%',
				'inherit',
				'smartypress_ai_alt_last_error',
				$wpdb->postmeta,
				'_wp_attachment_image_alt'
			)
		);

		return array(
			'total'       => $total,
			'with_alt'    => $with_alt,
			'without_alt' => max( 0, $total - $with_alt ),
			'generated'   => $generated,
			'failed'      => $failed,
		);
	}

	/**
	 * Generate metadata for one attachment.
	 *
	 * @param int                         $attachment_id Attachment ID.
	 * @param SmartyPress_AI_AI_Provider|null $provider      Vision provider instance.
	 * @param array                          $settings      Module settings.
	 * @param SmartyPress_AI_AI_Provider|null $text_provider Text provider instance.
	 * @return array
	 */
	public function generate_for_attachment( $attachment_id, $provider, array $settings, $text_provider = null ) {
		$attachment_id = absint( $attachment_id );

		if ( ! $attachment_id || ! wp_attachment_is_image( $attachment_id ) ) {
			return $this->error_result( $attachment_id, __( 'Attachment is not a valid image.', 'smartypress-ai-engine' ) );
		}

		$vision_mode = $settings['vision_mode'] ?? 'vision';
		$file        = get_attached_file( $attachment_id );

		if ( 'context' !== $vision_mode && ( ! $file || ! file_exists( $file ) || ! is_readable( $file ) ) ) {
			return $this->error_result( $attachment_id, __( 'Image file not found or not readable.', 'smartypress-ai-engine' ) );
		}

		if ( 'context' !== $vision_mode && ( ! $provider || ! method_exists( $provider, 'describe_image_with_prompt' ) ) ) {
			return $this->error_result( $attachment_id, __( 'Selected provider does not support image analysis for ALT generation.', 'smartypress-ai-engine' ) );
		}

		if ( ! $text_provider || ! method_exists( $text_provider, 'generate_content' ) ) {
			return $this->error_result( $attachment_id, __( 'Selected provider does not support text generation for ALT generation.', 'smartypress-ai-engine' ) );
		}

		$context            = $this->seo_context->resolve( $attachment_id );
		if ( ! $this->has_selected_metadata_fields( $settings ) ) {
			return $this->error_result( $attachment_id, __( 'No metadata fields were selected for generation.', 'smartypress-ai-engine' ) );
		}

		$requested_fields   = $this->get_requested_fields( $attachment_id, $settings );
		if ( empty( $requested_fields ) ) {
			delete_post_meta( $attachment_id, 'smartypress_ai_alt_last_error' );

			return array(
				'success'       => true,
				'attachment_id' => $attachment_id,
				'skipped'       => true,
				'message'       => __( 'All selected metadata fields are already filled.', 'smartypress-ai-engine' ),
				'item'          => $this->get_attachment_item( $attachment_id ),
			);
		}

		$generation_settings = $this->apply_requested_fields_to_settings( $settings, $requested_fields );
		$strategy_context    = $this->context_analyzer->analyze( $context, $generation_settings );
		$visual_description = '';

		if ( class_exists( 'SmartyPress_AI_Token_Tracker' ) ) {
			SmartyPress_AI_Token_Tracker::set_module( 'image-alt-generation' );
		}

		if ( 'context' !== $vision_mode ) {
			$vision_result = $provider->describe_image_with_prompt(
				$file,
				$this->prompt_builder->build_visual_description_prompt( $context, $generation_settings ),
				array(
					'model'       => $generation_settings['model'] ?? '',
					'max_tokens'  => 300,
					'temperature' => 0.1,
				)
			);

			if ( empty( $vision_result['success'] ) ) {
				return $this->error_result( $attachment_id, $vision_result['error'] ?? __( 'Image analysis failed.', 'smartypress-ai-engine' ) );
			}

			$visual_description = $this->clean_text( $vision_result['content'] ?? '' );
		}

		$prompt = 'context' === $vision_mode
			? $this->prompt_builder->build_context_only( $context, $generation_settings, $strategy_context )
			: $this->prompt_builder->build_metadata_prompt( $context, $generation_settings, $visual_description, $strategy_context );

		$result = $text_provider->generate_content(
			$prompt,
			array(
				'model'           => $generation_settings['text_model'] ?? '',
				'max_tokens'      => 500,
				'temperature'     => 0.2,
				'response_format' => array( 'type' => 'json_object' ),
				'system_prompt'   => __( 'You generate strict JSON metadata for WordPress images.', 'smartypress-ai-engine' ),
			)
		);

		if ( empty( $result['success'] ) ) {
			return $this->error_result( $attachment_id, $result['error'] ?? __( 'ALT generation failed.', 'smartypress-ai-engine' ) );
		}

		$metadata = $this->parse_metadata( $result['content'] ?? '' );
		$validation_error = $this->validate_generated_metadata( $metadata, $generation_settings );
		if ( '' !== $validation_error ) {
			return $this->error_result( $attachment_id, $validation_error );
		}

		$save_result = $this->save_metadata( $attachment_id, $metadata, $generation_settings, $text_provider, $strategy_context );
		if ( empty( $save_result['success'] ) ) {
			return $this->error_result( $attachment_id, $save_result['error'] ?? __( 'Generated metadata could not be saved.', 'smartypress-ai-engine' ) );
		}

		return array(
			'success'       => true,
			'attachment_id' => $attachment_id,
			'metadata'      => $metadata,
			'saved_fields'  => $save_result['saved_fields'],
			'strategy'      => $strategy_context,
			'item'          => $this->get_attachment_item( $attachment_id ),
		);
	}

	/**
	 * Get selected metadata fields that should be generated for one attachment.
	 *
	 * @param int   $attachment_id Attachment ID.
	 * @param array $settings      Settings.
	 * @return array
	 */
	private function get_requested_fields( $attachment_id, array $settings ) {
		$selected_fields = array();
		$field_flags     = array(
			'alt'         => 'generate_alt',
			'caption'     => 'generate_caption',
			'title'       => 'generate_title',
			'description' => 'generate_description',
		);

		foreach ( $field_flags as $field => $setting_key ) {
			if ( ! empty( $settings[ $setting_key ] ) ) {
				$selected_fields[] = $field;
			}
		}

		$current_values = $this->get_current_metadata_values( $attachment_id );

		return array_values(
			array_filter(
				$selected_fields,
				static function ( $field ) use ( $current_values ) {
					return '' === trim( (string) ( $current_values[ $field ] ?? '' ) );
				}
			)
		);
	}

	/**
	 * Check whether at least one metadata field is selected.
	 *
	 * @param array $settings Settings.
	 * @return bool
	 */
	private function has_selected_metadata_fields( array $settings ) {
		return ! empty( $settings['generate_alt'] )
			|| ! empty( $settings['generate_caption'] )
			|| ! empty( $settings['generate_title'] )
			|| ! empty( $settings['generate_description'] );
	}

	/**
	 * Apply requested fields as effective generation settings.
	 *
	 * @param array $settings         Original settings.
	 * @param array $requested_fields Requested field keys.
	 * @return array
	 */
	private function apply_requested_fields_to_settings( array $settings, array $requested_fields ) {
		$requested_fields = array_flip( $requested_fields );

		$settings['requested_fields']     = array_keys( $requested_fields );
		$settings['generate_alt']         = isset( $requested_fields['alt'] );
		$settings['generate_caption']     = isset( $requested_fields['caption'] );
		$settings['generate_title']       = isset( $requested_fields['title'] );
		$settings['generate_description'] = isset( $requested_fields['description'] );

		return $settings;
	}

	/**
	 * Get current metadata field values.
	 *
	 * @param int $attachment_id Attachment ID.
	 * @return array
	 */
	private function get_current_metadata_values( $attachment_id ) {
		$attachment = get_post( $attachment_id );

		return array(
			'alt'         => get_post_meta( $attachment_id, '_wp_attachment_image_alt', true ),
			'caption'     => $attachment ? $attachment->post_excerpt : '',
			'title'       => $attachment ? $attachment->post_title : '',
			'description' => $attachment ? $attachment->post_content : '',
		);
	}

	/**
	 * Build missing ALT meta query.
	 *
	 * @return array
	 */
	public function missing_alt_meta_query() {
		return array(
			'relation' => 'OR',
			array(
				'key'     => '_wp_attachment_image_alt',
				'compare' => 'NOT EXISTS',
			),
			array(
				'key'     => '_wp_attachment_image_alt',
				'value'   => '',
				'compare' => '=',
			),
		);
	}

	/**
	 * Return one formatted attachment item.
	 *
	 * @param int $attachment_id Attachment ID.
	 * @return array
	 */
	private function get_attachment_item( $attachment_id ) {
		$result = $this->get_attachments(
			array(
				'page'     => 1,
				'per_page' => 1,
				'search'   => (string) $attachment_id,
			)
		);

		foreach ( $result['items'] as $item ) {
			if ( absint( $item['id'] ) === absint( $attachment_id ) ) {
				return $item;
			}
		}

		$attachment = get_post( $attachment_id );
		$context    = $this->seo_context->resolve( $attachment_id );

		return array(
			'id'             => $attachment_id,
			'title'          => $attachment ? $attachment->post_title : '',
			'alt'            => get_post_meta( $attachment_id, '_wp_attachment_image_alt', true ),
			'caption'        => $attachment ? $attachment->post_excerpt : '',
			'description'    => $attachment ? $attachment->post_content : '',
			'url'            => wp_get_attachment_url( $attachment_id ),
			'thumbnail_url'  => wp_get_attachment_image_url( $attachment_id, 'thumbnail' ) ?: wp_get_attachment_url( $attachment_id ),
			'status'         => 'generated',
			'primary_post'   => $context['primary_post'],
			'focus_keywords' => $context['focus_keywords'],
			'strategy'       => get_post_meta( $attachment_id, 'smartypress_ai_alt_strategy', true ),
			'confidence'     => get_post_meta( $attachment_id, 'smartypress_ai_alt_confidence', true ),
			'needs_review'   => (bool) get_post_meta( $attachment_id, 'smartypress_ai_alt_needs_review', true ),
		);
	}

	/**
	 * Get user-facing generation status.
	 *
	 * Old failed attempts should not keep an image in failed state once ALT exists.
	 *
	 * @param int    $attachment_id Attachment ID.
	 * @param string $alt           Current ALT.
	 * @param string $error         Last generation error.
	 * @return string
	 */
	private function get_attachment_status( $attachment_id, $alt, $error ) {
		if ( '' !== trim( (string) $error ) && '' === trim( (string) $alt ) ) {
			return 'failed';
		}

		if ( get_post_meta( $attachment_id, 'smartypress_ai_alt_generated', true ) ) {
			return 'generated';
		}

		return 'idle';
	}

	/**
	 * Parse JSON response into metadata.
	 *
	 * @param string $content AI response.
	 * @return array
	 */
	private function parse_metadata( $content ) {
		$content = trim( (string) $content );
		$content = preg_replace( '/^```(?:json)?\s*|\s*```$/', '', $content );
		$data    = json_decode( $content, true );

		if ( ! is_array( $data ) ) {
			$data = array( 'alt' => wp_strip_all_tags( $content ) );
		}

		return array(
			'alt'         => $this->clean_text( $data['alt'] ?? '' ),
			'caption'     => $this->clean_text( $data['caption'] ?? '' ),
			'title'       => $this->clean_text( $data['title'] ?? '' ),
			'description' => $this->clean_text( $data['description'] ?? '' ),
		);
	}

	/**
	 * Validate only the metadata fields enabled in settings.
	 *
	 * @param array $metadata Generated metadata.
	 * @param array $settings Module settings.
	 * @return string Error message or empty string.
	 */
	private function validate_generated_metadata( array $metadata, array $settings ) {
		if ( ! empty( $settings['generate_alt'] ) && empty( $metadata['alt'] ) ) {
			return __( 'AI response did not include ALT text.', 'smartypress-ai-engine' );
		}

		if ( ! empty( $settings['generate_caption'] ) && empty( $metadata['caption'] ) ) {
			return __( 'AI response did not include caption.', 'smartypress-ai-engine' );
		}

		if ( ! empty( $settings['generate_title'] ) && empty( $metadata['title'] ) ) {
			return __( 'AI response did not include title.', 'smartypress-ai-engine' );
		}

		if ( ! empty( $settings['generate_description'] ) && empty( $metadata['description'] ) ) {
			return __( 'AI response did not include description.', 'smartypress-ai-engine' );
		}

		return '';
	}

	/**
	 * Save generated metadata.
	 *
	 * @param int                        $attachment_id Attachment ID.
	 * @param array                      $metadata      Generated metadata.
	 * @param array                      $settings      Settings.
	 * @param SmartyPress_AI_AI_Provider $provider      Provider.
	 * @return array
	 */
	private function save_metadata( $attachment_id, array $metadata, array $settings, $provider, array $strategy_context = array() ) {
		$saved_fields = array();

		if ( ! empty( $settings['generate_alt'] ) && '' !== $metadata['alt'] ) {
			update_post_meta( $attachment_id, '_wp_attachment_image_alt', $metadata['alt'] );
			$saved_fields[] = 'alt';
		}

		$post_data = array( 'ID' => $attachment_id );
		if ( ! empty( $settings['generate_caption'] ) && '' !== $metadata['caption'] ) {
			$post_data['post_excerpt'] = $metadata['caption'];
			$saved_fields[] = 'caption';
		}
		if ( ! empty( $settings['generate_title'] ) && '' !== $metadata['title'] ) {
			$post_data['post_title'] = $metadata['title'];
			$saved_fields[] = 'title';
		}
		if ( ! empty( $settings['generate_description'] ) && '' !== $metadata['description'] ) {
			$post_data['post_content'] = $metadata['description'];
			$saved_fields[] = 'description';
		}

		if ( count( $post_data ) > 1 ) {
			$updated = wp_update_post( wp_slash( $post_data ), true );
			if ( is_wp_error( $updated ) ) {
				return array(
					'success' => false,
					'error'   => sprintf(
						/* translators: %s: WordPress error message. */
						__( 'Generated metadata could not be saved: %s', 'smartypress-ai-engine' ),
						$updated->get_error_message()
					),
				);
			}
		}

		if ( empty( $saved_fields ) ) {
			return array(
				'success' => false,
				'error'   => __( 'No metadata fields were selected for generation.', 'smartypress-ai-engine' ),
			);
		}

		update_post_meta( $attachment_id, 'smartypress_ai_alt_generated', 1 );
		update_post_meta( $attachment_id, 'smartypress_ai_alt_generated_at', current_time( 'mysql' ) );
		update_post_meta( $attachment_id, 'smartypress_ai_alt_provider', method_exists( $provider, 'get_provider_name' ) ? $provider->get_provider_name() : '' );
		update_post_meta( $attachment_id, 'smartypress_ai_alt_model', sanitize_text_field( $settings['text_model'] ?? '' ) );
		update_post_meta( $attachment_id, 'smartypress_ai_alt_vision_model', sanitize_text_field( $settings['model'] ?? '' ) );
		update_post_meta( $attachment_id, 'smartypress_ai_alt_strategy', sanitize_key( $strategy_context['strategy'] ?? '' ) );
		update_post_meta( $attachment_id, 'smartypress_ai_alt_confidence', sanitize_key( $strategy_context['confidence'] ?? '' ) );
		update_post_meta( $attachment_id, 'smartypress_ai_alt_needs_review', ! empty( $strategy_context['needs_review'] ) ? 1 : 0 );
		delete_post_meta( $attachment_id, 'smartypress_ai_alt_last_error' );

		return array(
			'success'      => true,
			'saved_fields' => $saved_fields,
		);
	}

	/**
	 * Store and return an error.
	 *
	 * @param int    $attachment_id Attachment ID.
	 * @param string $message       Error message.
	 * @return array
	 */
	private function error_result( $attachment_id, $message ) {
		if ( $attachment_id ) {
			update_post_meta( $attachment_id, 'smartypress_ai_alt_last_error', $message );
		}

		return array(
			'success'       => false,
			'attachment_id' => $attachment_id,
			'error'         => $message,
		);
	}

	/**
	 * Clean one text field.
	 *
	 * @param string $text Text.
	 * @return string
	 */
	private function clean_text( $text ) {
		$text = wp_strip_all_tags( (string) $text );
		$text = preg_replace( '/\s+/u', ' ', trim( $text ) );
		return html_entity_decode( $text, ENT_QUOTES, get_bloginfo( 'charset' ) );
	}
}
