<?php
namespace Smart_Media_Cleanup;

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

class Orphan_Scanner {

	const TRANSIENT_COUNT = 'smc_orphan_count';
	const TRANSIENT_SCAN  = 'smc_scan_results';

	public function __construct() {}

	/**
	 * Returns a paginated slice of orphan attachments using a bulk scan.
	 *
	 * On the first call (offset = 0) the full bulk query runs and the complete
	 * list of orphan IDs is stored in a short-lived transient. Subsequent calls
	 * read from the transient — effectively instant — so the AJAX progress batches
	 * stay responsive without hammering the database.
	 *
	 * Returns array with:
	 *   'items' => array of orphan metadata objects for this slice
	 *   'total' => total number of orphans found in this scan
	 */
	public function scan( $offset = 0, $limit = 50 ) {
		$offset = absint( $offset );
		$limit  = absint( $limit ) ?: 50;

		// Always start fresh on the first batch.
		if ( 0 === $offset ) {
			delete_transient( self::TRANSIENT_SCAN );
		}

		$orphan_ids = get_transient( self::TRANSIENT_SCAN );

		if ( false === $orphan_ids ) {
			$used_ids   = Attachment_Usage::get_all_used_ids();
			$all_ids    = $this->get_all_attachment_ids();
			$orphan_ids = array_values( array_diff( $all_ids, $used_ids ) );
			set_transient( self::TRANSIENT_SCAN, $orphan_ids, 15 * MINUTE_IN_SECONDS );
		}

		$slice = array_slice( $orphan_ids, $offset, $limit );
		$items = array();

		foreach ( $slice as $id ) {
			$file  = get_attached_file( $id );
			$size  = $file && file_exists( $file ) ? filesize( $file ) : 0;

			$items[] = array(
				'id'             => $id,
				'title'          => get_the_title( $id ),
				'filename'       => $file ? basename( $file ) : '',
				'url'            => wp_get_attachment_url( $id ),
				'thumbnail_url'  => wp_get_attachment_thumb_url( $id ) ?: wp_get_attachment_url( $id ),
				'filesize'       => $size,
				'filesize_human' => $size ? size_format( $size ) : '—',
				'upload_date'    => get_the_date( 'Y-m-d', $id ),
			);
		}

		return array(
			'items' => $items,
			'total' => count( $orphan_ids ),
		);
	}

	/**
	 * Returns total number of attachments in the library (for use outside scan context).
	 */
	public function count_total() {
		$count = wp_count_posts( 'attachment' );
		return isset( $count->inherit ) ? (int) $count->inherit : 0;
	}

	/**
	 * Returns cached orphan count. Uses the same bulk method as scan().
	 */
	public function count_orphans() {
		$cached = get_transient( self::TRANSIENT_COUNT );
		if ( false !== $cached ) {
			return (int) $cached;
		}

		$used_ids = Attachment_Usage::get_all_used_ids();
		$all_ids  = $this->get_all_attachment_ids();
		$count    = count( array_diff( $all_ids, $used_ids ) );

		set_transient( self::TRANSIENT_COUNT, $count, HOUR_IN_SECONDS );
		return $count;
	}

	/**
	 * Deletes a batch of attachments by ID.
	 * Each attachment is re-checked with is_used_elsewhere() before deletion —
	 * this is the safety net that catches anything the bulk scan may have missed.
	 */
	public function delete_batch( array $ids ) {
		$result = array(
			'deleted' => array(),
			'skipped' => array(),
			'errors'  => array(),
		);

		$logger = Plugin::instance()->get_logger();

		foreach ( $ids as $id ) {
			$id = absint( $id );
			if ( ! $id ) {
				continue;
			}

			if ( 'attachment' !== get_post_type( $id ) ) {
				$result['skipped'][] = $id;
				continue;
			}

			// Final per-attachment safety check before deleting.
			if ( Attachment_Usage::is_used_elsewhere( $id, 0 ) ) {
				$result['skipped'][] = $id;
				continue;
			}

			$deleted = wp_delete_attachment( $id, true );

			if ( $deleted ) {
				$result['deleted'][] = $id;
				$logger->log( 'orphan_delete', $id, null, 'Deleted via orphan cleanup.' );
			} else {
				$result['errors'][] = $id;
				$logger->log( 'error', $id, null, 'Failed to delete orphan attachment.' );
			}
		}

		delete_transient( self::TRANSIENT_COUNT );
		delete_transient( self::TRANSIENT_SCAN );

		return $result;
	}

	/**
	 * Returns all attachment IDs in the library, ordered by ID.
	 */
	private function get_all_attachment_ids() {
		global $wpdb;
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery
		$ids = $wpdb->get_col(
			"SELECT ID FROM {$wpdb->posts}
			WHERE post_type = 'attachment'
			AND post_status = 'inherit'
			ORDER BY ID ASC"
		);
		return array_map( 'intval', (array) $ids );
	}
}
