<?php
namespace R2_Offload\Media;

use R2_Offload\DB;
use R2_Offload\Logger;
use R2_Offload\Queue\Scheduler;
use R2_Offload\Settings;
use R2_Offload\Storage\Storage_Client;

defined( 'ABSPATH' ) || exit;

final class Offloader {

	private Storage_Client $storage;
	private Settings $settings;
	private Logger $logger;
	private Scheduler $queue;

	public function __construct( Storage_Client $storage, Settings $settings, Logger $logger, Scheduler $queue ) {
		$this->storage  = $storage;
		$this->settings = $settings;
		$this->logger   = $logger;
		$this->queue    = $queue;
	}

	public function register(): void {
		add_action( 'wp_generate_attachment_metadata', [ $this, 'on_metadata' ], 20, 2 );
		add_action( 'r2_offload_process_attachment', [ $this, 'process_attachment' ], 10, 1 );
		add_action( 'delete_attachment', [ $this, 'on_delete' ], 10, 1 );
	}

	public function on_metadata( array $metadata, int $attachment_id ): array {
		if ( ! $this->settings->get( 'offload_on_upload' ) ) {
			return $metadata;
		}
		if ( ! $this->storage->is_available() ) {
			$this->logger->warn( 'Storage not configured; skipping offload.', 'offload', [ 'attachment' => $attachment_id ] );
			return $metadata;
		}

		DB::upsert_item(
			$attachment_id,
			[
				'status'        => 'pending',
				'provider'      => $this->settings->provider(),
				'bucket'        => (string) $this->settings->get( 'bucket' ),
				'object_prefix' => $this->settings->effective_prefix(),
				'updated_at'    => current_time( 'mysql' ),
			]
		);
		$this->queue->enqueue_attachment( $attachment_id );
		return $metadata;
	}

	public function process_attachment( int $attachment_id ): void {
		$this->logger->info( 'process_attachment start', 'offload', [ 'attachment' => $attachment_id ] );

		if ( ! $this->storage->is_available() ) {
			$reason = 'Storage not configured (SDK missing or bucket/creds empty).';
			DB::set_status( $attachment_id, 'failed', $reason );
			$this->logger->error( $reason, 'offload', [ 'attachment' => $attachment_id ] );
			return;
		}

		$paths = Path_Resolver::attachment_files( $attachment_id );
		if ( empty( $paths ) ) {
			DB::set_status( $attachment_id, 'skipped', 'No local files found.' );
			$this->logger->warn( 'Skipped: no local files.', 'offload', [ 'attachment' => $attachment_id ] );
			return;
		}

		$this->logger->info( 'process_attachment paths resolved', 'offload', [ 'attachment' => $attachment_id, 'count' => count( $paths ), 'first' => basename( (string) $paths[0] ) ] );
		DB::set_status( $attachment_id, 'processing' );

		$uploads = wp_get_upload_dir();
		$base    = trailingslashit( $uploads['basedir'] );
		$prefix  = $this->settings->effective_prefix();

		try {
			foreach ( $paths as $local ) {
				$relative   = ltrim( str_replace( $base, '', $local ), '/' );
				$object_key = $prefix !== '' ? $prefix . '/' . $relative : $relative;
				$mime       = function_exists( 'mime_content_type' ) ? (string) @mime_content_type( $local ) : '';
				$this->storage->upload_file( $local, $object_key, $mime );
			}
			DB::set_status( $attachment_id, 'offloaded', null );
			update_post_meta( $attachment_id, '_r2_offload_status', 'offloaded' );
			$this->logger->info( 'Offloaded attachment ' . $attachment_id, 'offload', [ 'files' => count( $paths ) ] );
			do_action( 'r2_offload_after_offloaded', $attachment_id, $paths );
		} catch ( \Throwable $e ) {
			DB::set_status( $attachment_id, 'failed', $e->getMessage() );
			$this->logger->error( 'Offload failed: ' . $e->getMessage(), 'offload', [ 'attachment' => $attachment_id ] );
		}
	}

	public function on_delete( int $attachment_id ): void {
		$item = DB::get_item( $attachment_id );
		if ( ! $item || $item->status !== 'offloaded' ) {
			return;
		}
		if ( ! $this->storage->is_available() ) {
			return;
		}

		$uploads = wp_get_upload_dir();
		$base    = trailingslashit( $uploads['basedir'] );
		$prefix  = trim( (string) $item->object_prefix, '/' );

		// Prefer on-disk discovery; fall back to metadata-derived keys when local
		// copies were pruned (otherwise remote objects would be orphaned).
		$paths     = Path_Resolver::attachment_files( $attachment_id );
		$relatives = [];
		if ( ! empty( $paths ) ) {
			foreach ( $paths as $local ) {
				$relatives[] = ltrim( str_replace( $base, '', $local ), '/' );
			}
		} else {
			$relatives = Path_Resolver::relative_paths_from_meta( $attachment_id, $uploads['basedir'] );
		}

		foreach ( array_unique( $relatives ) as $relative ) {
			$object_key = $prefix !== '' ? $prefix . '/' . $relative : $relative;
			$this->storage->delete_object( $object_key );
		}
	}
}
