<?php
/**
 * Media-action AJAX handlers — delete unused/missing/broken, add-to-media.
 *
 * @package CoffeeWP_Media_Cleanup
 */

namespace CWPM\Ajax;

use CWPM\Core\Db;
use CWPM\Core\Logger;
use CWPM\Core\Trash;
use CWPM\Service\MediaService;

defined( 'ABSPATH' ) || exit;

// phpcs:disable WordPress.Security.NonceVerification.Missing -- nonce verified centrally in BaseHandler::verify_request().

class MediaHandler extends BaseHandler {

	/**
	 * Constructor — register AJAX actions.
	 */
	public function __construct() {
		add_action( 'wp_ajax_cwpm_delete_unused', array( $this, 'delete_unused' ) );
		add_action( 'wp_ajax_cwpm_delete_missing', array( $this, 'delete_missing' ) );
		add_action( 'wp_ajax_cwpm_delete_broken', array( $this, 'delete_broken' ) );
		add_action( 'wp_ajax_cwpm_add_to_media', array( $this, 'add_to_media' ) );
	}

	/**
	 * Soft-delete an unused attachment.
	 *
	 * The physical file is moved to wp-content/cwpm-trash/ via Core\Trash,
	 * and the post_type is changed from 'attachment' to 'cwpm-trash' so the
	 * record remains visible in the plugin's trash view.
	 */
	public function delete_unused() {
		check_ajax_referer( 'cwpm_nonce', 'nonce' );
		$this->verify_request();

		$attachment_id = absint( $_POST['attachment_id'] ?? 0 );

		if ( ! $attachment_id ) {
			wp_send_json_error( 'Missing attachment_id', 400 );
		}

		try {
			$result = MediaService::delete_unused( $attachment_id );
			wp_send_json_success( $result );
		} catch ( \RuntimeException $e ) {
			wp_send_json_error( $e->getMessage(), 400 );
		}
	}

	/**
	 * Delete an orphan file that exists on disk but has no Media Library entry.
	 * The file is moved to the plugin trash directory for safe recovery.
	 */
	public function delete_missing() {
		check_ajax_referer( 'cwpm_nonce', 'nonce' );
		$this->verify_request();

		$rel_path = isset( $_POST['rel_path'] )
			? sanitize_text_field( wp_unslash( $_POST['rel_path'] ) )
			: '';

		if ( empty( $rel_path ) ) {
			wp_send_json_error( 'Missing rel_path', 400 );
		}

		$uploads_base = wp_upload_dir()['basedir'];
		$abs_path     = $uploads_base . '/' . ltrim( $rel_path, '/' );

		if ( ! file_exists( $abs_path ) || ! is_file( $abs_path ) ) {
			wp_send_json_error( 'File not found', 400 );
		}

		$rel_dir = dirname( $rel_path );
		if ( '.' === $rel_dir ) {
			$rel_dir = '';
		}

		$moved = Trash::move_to_trash( $abs_path, $rel_dir );

		if ( ! $moved ) {
			Logger::error( 'Failed to trash missing file: ' . $rel_path );
			wp_send_json_error( 'Failed to move file to trash', 500 );
		}

		Db::update_result_action( $rel_path, 'delete-missing', 'trashed' );

		Logger::info( 'Missing file trashed: ' . $rel_path );

		wp_send_json_success( array( 'rel_path' => $rel_path ) );
	}

	/**
	 * Move a broken attachment record (DB entry with no file on disk) to trash.
	 *
	 * Inserts a DB-only cwpm_trash row + soft-deletes the attachment to
	 * post_type='cwpm-trash'. Surfaces in the Trash UI for restore/permanent
	 * delete via the same flow as trash-file on broken records — keeping all
	 * destructive actions consistent and recoverable.
	 */
	public function delete_broken() {

		global $wpdb;

		check_ajax_referer( 'cwpm_nonce', 'nonce' );
		$this->verify_request();

		$attachment_id = absint( $_POST['attachment_id'] ?? 0 );

		if ( ! $attachment_id ) {
			wp_send_json_error( 'Missing attachment_id', 400 );
		}

		$post = get_post( $attachment_id );

		if ( ! $post || 'attachment' !== $post->post_type ) {
			wp_send_json_error( 'Invalid attachment', 400 );
		}

		$rel_path = get_post_meta( $attachment_id, '_wp_attached_file', true );

		if ( $rel_path ) {
			Db::insert_trash_record(
				array(
					'rel_path'      => $rel_path,
					'original_path' => $rel_path,
					'file_name'     => basename( $rel_path ),
					'file_size'     => 0,
				)
			);
		}

		// phpcs:ignore WordPress.DB.DirectDatabaseQuery -- tier-2 soft-delete via post_type swap; no WP API equivalent that bypasses trash-bin meta.
		$wpdb->update(
			$wpdb->posts,
			array( 'post_type' => 'cwpm-trash' ),
			array( 'ID' => $attachment_id ),
			array( '%s' ),
			array( '%d' )
		);
		clean_post_cache( $attachment_id );

		if ( $rel_path ) {
			Db::update_result_action( $rel_path, 'delete-broken', 'trashed' );
		}

		Logger::info( 'Broken entry trashed: ' . $rel_path . ' (attachment #' . $attachment_id . ')' );

		wp_send_json_success( array( 'item_id' => $attachment_id ) );
	}

	/**
	 * Create a Media Library attachment record for an orphan file on disk.
	 *
	 * Expects POST:
	 *   rel_path — string, path relative to the uploads basedir.
	 *
	 * Returns: { rel_path: string, new_id: int }
	 */
	public function add_to_media() {
		check_ajax_referer( 'cwpm_nonce', 'nonce' );
		$this->verify_request( 'upload_files' );

		$rel_path = isset( $_POST['rel_path'] )
			? sanitize_text_field( wp_unslash( $_POST['rel_path'] ) )
			: '';

		if ( empty( $rel_path ) ) {
			wp_send_json_error( 'Missing rel_path', 400 );
		}

		$uploads_base = wp_upload_dir()['basedir'];

		try {
			$result = MediaService::add_to_media( $rel_path, $uploads_base );
			wp_send_json_success( $result );
		} catch ( \RuntimeException $e ) {
			wp_send_json_error( $e->getMessage(), 400 );
		}
	}
}
