<?php

/**
 * ContentHandler — Domain handler for WordPress content (posts/pages) CRUD.
 *
 * Owns: get_content, create_content, edit_content, delete_content, export_content
 *
 * @package LolaCore\Actions\Handlers
 * @license GPL-2.0-or-later
 */

// phpcs:disable WordPress.WP.AlternativeFunctions.file_system_operations_file_put_contents -- Required for WXR export to uploads directory.

declare( strict_types=1 );

namespace LolaCore\Actions\Handlers;

use LolaCore\Actions\Contracts\ActionHandlerInterface;

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

/**
 * Handles get_content, create_content, edit_content, delete_content, export_content.
 */
class ContentHandler implements ActionHandlerInterface {

	private const MAX_BULK    = 50;
	private const MAX_CONTENT = 50;

	/** @inheritDoc */
	public function getSupportedActions(): array {
		return array( 'get_content', 'create_content', 'edit_content', 'delete_content', 'export_content' );
	}

	/** @inheritDoc */
	public function handle( string $actionId, array $params, bool $dryRun ): array {
		return match ( $actionId ) {
			'get_content'    => $this->getContent( $params ),
			'create_content' => $this->createContent( $params, $dryRun ),
			'edit_content'   => $this->editContent( $params, $dryRun ),
			'delete_content' => $this->deleteContent( $params, $dryRun ),
			'export_content' => $this->exportContent( $params ),
			default          => throw new \Exception( esc_html( "ContentHandler: unknown action '{$actionId}'." ) ),
		};
	}

	private function getContent( array $params ): array {
		$args = array(
			'post_type'      => $params['post_type'] ?? 'post',
			'post_status'    => $params['status'] ?? 'publish',
			'posts_per_page' => min( (int) ( $params['limit'] ?? 10 ), self::MAX_CONTENT ),
		);

		if ( ! empty( $params['search'] ) ) {
			$args['s'] = sanitize_text_field( $params['search'] );
		}

		$posts = get_posts( $args );
		$data  = array();
		foreach ( $posts as $p ) {
			$body   = wp_strip_all_tags( $p->post_content );
			$data[] = array(
				'id'        => $p->ID,
				'title'     => $p->post_title,
				'slug'      => $p->post_name,
				'post_type' => $p->post_type,
				'status'    => $p->post_status,
				'date'      => $p->post_date,
				'modified'  => $p->post_modified,
				'excerpt'   => $p->post_excerpt,
				'content'   => mb_strlen( $body ) > 2000 ? mb_substr( $body, 0, 2000 ) . '... [truncated]' : $body,
				'url'       => get_permalink( $p->ID ),
			);
		}
		return array( 'posts' => $data, 'total' => count( $data ) );
	}

	private function createContent( array $params, bool $dryRun ): array {
		$title          = $params['title'] ?? 'Draft Post';
		$content        = $params['content'] ?? '';
		$type           = $params['type'] ?? 'post';
		$status         = $params['status'] ?? 'draft';
		$categories     = $params['categories'] ?? null;
		$tags           = $params['tags'] ?? null;
		$featured_image = isset( $params['featured_image'] ) ? (int) $params['featured_image'] : null;

		if ( $dryRun ) {
			return array(
				'preview' => array(
					'title'          => $title,
					'type'           => $type,
					'status'         => $status,
					'content'        => mb_substr( $content, 0, 100 ) . '...',
					'categories'     => $categories,
					'tags'           => $tags,
					'featured_image' => $featured_image,
				),
			);
		}

		$post_data = array(
			'post_title'   => $title,
			'post_content' => $content,
			'post_type'    => $type,
			'post_status'  => $status,
		);

		if ( is_array( $categories ) ) {
			$post_data['post_category'] = array_map( 'intval', $categories );
		}
		if ( is_array( $tags ) ) {
			$post_data['tags_input'] = $tags;
		}

		$post_id = wp_insert_post( $post_data, true );
		if ( is_wp_error( $post_id ) ) {
			// phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped
			throw new \Exception( $post_id->get_error_message() );
		}

		if ( $featured_image !== null && $featured_image > 0 ) {
			set_post_thumbnail( $post_id, $featured_image );
		}

		return array(
			'success' => true,
			'post_id' => $post_id,
			'url'     => get_permalink( $post_id ),
		);
	}

	private function editContent( array $params, bool $dryRun ): array {
		$post_id = (int) ( $params['post_id'] ?? 0 );
		if ( $post_id <= 0 ) {
			throw new \Exception( 'A valid post_id is required.' );
		}

		$post = get_post( $post_id );
		if ( ! $post ) {
			// phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped
			throw new \Exception( esc_html( sprintf( 'Post with ID %d not found.', intval( $post_id ) ) ) );
		}

		$editable = array(
			'title'   => 'post_title',
			'content' => 'post_content',
			'status'  => 'post_status',
			'excerpt' => 'post_excerpt',
			'slug'    => 'post_name',
		);
		$changes  = array();
		foreach ( $editable as $param_key => $wp_key ) {
			if ( isset( $params[ $param_key ] ) ) {
				$changes[ $wp_key ] = $params[ $param_key ];
			}
		}

		if ( isset( $params['categories'] ) && is_array( $params['categories'] ) ) {
			$changes['post_category'] = array_map( 'intval', $params['categories'] );
		}
		if ( isset( $params['tags'] ) && is_array( $params['tags'] ) ) {
			$changes['tags_input'] = $params['tags'];
		}

		$has_featured = isset( $params['featured_image'] );
		if ( empty( $changes ) && ! $has_featured ) {
			throw new \Exception( 'No fields to update. Provide at least one of: title, content, status, excerpt, slug, categories, tags, or featured_image.' );
		}

		if ( $dryRun ) {
			$diff = array();
			foreach ( $changes as $wp_key => $new_val ) {
				$current      = $post->$wp_key ?? '';
				$diff[$wp_key] = array(
					'current'  => mb_substr( is_array( $current ) ? wp_json_encode( $current ) : (string) $current, 0, 200 ),
					'proposed' => mb_substr( is_array( $new_val ) ? wp_json_encode( $new_val ) : (string) $new_val, 0, 200 ),
				);
			}
			if ( $has_featured ) {
				$diff['featured_image'] = array(
					'current'  => get_post_thumbnail_id( $post_id ),
					'proposed' => $params['featured_image'],
				);
			}
			return array( 'post_id' => $post_id, 'post_title' => $post->post_title, 'post_type' => $post->post_type, 'changes' => $diff );
		}

		if ( ! empty( $changes ) ) {
			$update_data       = $changes;
			$update_data['ID'] = $post_id;
			$result            = wp_update_post( $update_data, true );
			if ( is_wp_error( $result ) ) {
				// phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped
				throw new \Exception( 'Update failed: ' . $result->get_error_message() );
			}
		}

		if ( $has_featured ) {
			$fi = (int) $params['featured_image'];
			$fi === 0 ? delete_post_thumbnail( $post_id ) : set_post_thumbnail( $post_id, $fi );
			$changes['featured_image'] = $fi;
		}

		return array(
			'success'        => true,
			'post_id'        => $post_id,
			'fields_updated' => array_keys( $changes ),
			'url'            => get_permalink( $post_id ),
		);
	}

	private function deleteContent( array $params, bool $dryRun ): array {
		$post_ids  = $params['post_ids'] ?? array();
		$permanent = (bool) ( $params['permanent'] ?? false );

		if ( empty( $post_ids ) || ! is_array( $post_ids ) ) {
			throw new \Exception( 'An array of post_ids is required.' );
		}
		if ( count( $post_ids ) > self::MAX_BULK ) {
			// phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped
			throw new \Exception( 'Maximum 50 posts per operation. Received ' . count( $post_ids ) . '.' );
		}

		$post_ids = array_map( 'intval', $post_ids );
		$details  = array();
		foreach ( $post_ids as $id ) {
			$post = get_post( $id );
			if ( $post ) {
				$details[] = array( 'id' => $id, 'title' => $post->post_title, 'type' => $post->post_type, 'status' => $post->post_status, 'date' => $post->post_date );
			}
		}

		if ( $dryRun ) {
			return array(
				'action'  => $permanent ? 'Will permanently delete' : 'Will move to trash',
				'count'   => count( $details ),
				'posts'   => $details,
				'warning' => $permanent ? 'This is IRREVERSIBLE. Ensure you have a backup.' : 'Posts can be recovered from trash.',
			);
		}

		$deleted = 0;
		$errors  = array();
		foreach ( $post_ids as $id ) {
			$result = $permanent ? wp_delete_post( $id, true ) : wp_trash_post( $id );
			$result ? $deleted++ : $errors[] = $id;
		}

		return array(
			'success' => $deleted > 0,
			'deleted' => $deleted,
			'failed'  => $errors,
			'message' => $permanent ? "{$deleted} post(s) permanently deleted." : "{$deleted} post(s) moved to trash.",
		);
	}

	private function exportContent( array $params ): array {
		$post_type  = $params['post_type'] ?? 'post';
		$status     = $params['status'] ?? 'all';
		$start_date = $params['start_date'] ?? '';
		$end_date   = $params['end_date'] ?? '';

		$args = array(
			'post_type'      => sanitize_text_field( $post_type ),
			'posts_per_page' => -1,
			'post_status'    => $status !== 'all' ? sanitize_text_field( $status ) : array( 'publish', 'draft', 'pending', 'private' ),
		);

		if ( ! empty( $start_date ) ) {
			$args['date_query'][] = array( 'after' => sanitize_text_field( $start_date ) );
		}
		if ( ! empty( $end_date ) ) {
			$args['date_query'][] = array( 'before' => sanitize_text_field( $end_date ) );
		}

		$posts = get_posts( $args );
		$count = count( $posts );

		if ( $count === 0 ) {
			return array( 'success' => false, 'message' => "No {$post_type} found matching the criteria.", 'count' => 0 );
		}

		$site_url   = get_bloginfo( 'url' );
		$site_title = get_bloginfo( 'name' );
		$xml        = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
		$xml       .= "<rss version=\"2.0\" xmlns:excerpt=\"http://wordpress.org/export/1.2/excerpt/\" ";
		$xml       .= "xmlns:content=\"http://purl.org/rss/1.0/modules/content/\" ";
		$xml       .= "xmlns:wp=\"http://wordpress.org/export/1.2/\">\n<channel>\n";
		$xml       .= "<title>" . esc_xml( $site_title ) . "</title>\n";
		$xml       .= "<link>{$site_url}</link>\n<wp:wxr_version>1.2</wp:wxr_version>\n";

		foreach ( $posts as $p ) {
			$xml .= "<item>\n";
			$xml .= "  <title>" . esc_xml( $p->post_title ) . "</title>\n";
			$xml .= "  <link>" . esc_url( get_permalink( $p->ID ) ) . "</link>\n";
			$xml .= "  <wp:post_id>{$p->ID}</wp:post_id>\n";
			$xml .= "  <wp:post_date>" . esc_xml( $p->post_date ) . "</wp:post_date>\n";
			$xml .= "  <wp:post_type>" . esc_xml( $p->post_type ) . "</wp:post_type>\n";
			$xml .= "  <wp:status>" . esc_xml( $p->post_status ) . "</wp:status>\n";
			$xml .= "  <content:encoded><![CDATA[{$p->post_content}]]></content:encoded>\n";
			$xml .= "  <excerpt:encoded><![CDATA[{$p->post_excerpt}]]></excerpt:encoded>\n";
			$xml .= "</item>\n";
		}
		$xml .= "</channel>\n</rss>";

		$upload_dir = wp_upload_dir();
		$filename   = 'lolacore-export-' . gmdate( 'Ymd-His' ) . '.xml';
		$filepath   = $upload_dir['basedir'] . '/' . $filename;
		$fileurl    = $upload_dir['baseurl'] . '/' . $filename;

		$written = file_put_contents( $filepath, $xml );
		if ( ! $written ) {
			throw new \Exception( 'Failed to write export file. Check upload directory permissions.' );
		}

		wp_schedule_single_event( time() + 3600, 'lolacore_cleanup_export', array( $filepath ) );

		return array(
			'success'      => true,
			'post_type'    => $post_type,
			'count'        => $count,
			'download_url' => $fileurl,
			'filename'     => $filename,
			'expires_in'   => '1 hour',
			'message'      => "Exported {$count} {$post_type}(s). Download link valid for 1 hour.",
		);
	}
}
