<?php
/**
 * File Path: includes/admin/class-mbs-metabox-arc-ajax.php
 *
 * Pre-Release / ARC Metabox — AJAX Handlers
 *
 * Handles the four AJAX actions for the ARC metabox:
 *   mbs_export_arc_reviewers      — XLSX / CSV export
 *   mbs_export_arc_reviewers_json — JSON export (F-027)
 *   mbs_arc_approve_applicant     — approve a pending applicant
 *   mbs_arc_reject_applicant      — reject a pending applicant
 *
 * Extracted from MBS_Metabox_Arc (REF-015, Session 137).
 *
 * @package MyBookShowroom
 * @since   1.1.0
 */

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

class MBS_Metabox_Arc_Ajax { // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedClassFound -- mbs_/MBS_ prefix is correct; scanner not configured with plugin prefix

	public function __construct() {
		add_action( 'wp_ajax_mbs_export_arc_reviewers',      array( $this, 'ajax_export' ) );
		add_action( 'wp_ajax_mbs_export_arc_reviewers_json', array( $this, 'ajax_export_json' ) );
		add_action( 'wp_ajax_mbs_arc_approve_applicant',     array( $this, 'ajax_approve_applicant' ) );
		add_action( 'wp_ajax_mbs_arc_reject_applicant',      array( $this, 'ajax_reject_applicant'  ) );
	}

	/** AJAX export handler — streams XLSX or CSV file download. */
	public function ajax_export(): void {
		$post_id = absint( $_POST['post_id'] ?? 0 );
		$format  = sanitize_key( $_POST['format'] ?? 'xlsx' );
		$nonce   = sanitize_text_field( wp_unslash( $_POST['nonce'] ?? '' ) );

		if ( ! wp_verify_nonce( $nonce, 'mbs_export_arc_' . $post_id ) ) {
			wp_send_json_error( 'Invalid nonce', 403 );
		}
		if ( ! current_user_can( 'edit_post', $post_id ) ) {
			wp_send_json_error( 'Insufficient permissions', 403 );
		}
		if ( ! mbs_is_pro() ) {
			wp_send_json_error( 'PRO license required', 403 );
		}

		$reviewers         = MBS_Arc_Recipient_DAO::get_by_book( $post_id );
		$urls_by_recipient = MBS_Arc_Recipient_DAO::get_urls_for_book( $post_id ); // R2-004
		$slug              = get_post_field( 'post_name', $post_id ) ?: 'book-' . $post_id;
		$date              = gmdate( 'Y-m-d' );

		// Build URL list per reviewer from the child table (R2-004).
		// Falls back to legacy review_url for rows that pre-date migration 4.28.
		$all_urls = array();
		foreach ( $reviewers as $r ) {
			$urls = $urls_by_recipient[ (int) $r->id ] ?? array();
			if ( empty( $urls ) && ! empty( $r->review_url ) ) {
				$urls = array( $r->review_url );
			}
			$all_urls[] = $urls;
		}

		// Find max URL count to determine column width.
		$max_urls = 1;
		foreach ( $all_urls as $urls ) {
			$max_urls = max( $max_urls, count( $urls ) );
		}

		// Build rows array (headers + data).
		$header = array( 'Reviewer Name', 'Contact Method', 'Date Sent', 'Review Posted' );
		for ( $u = 1; $u <= $max_urls; $u++ ) {
			$header[] = $max_urls > 1 ? 'Review URL ' . $u : 'Review URL';
		}
		$rows   = array();
		$rows[] = $header;
		foreach ( $reviewers as $i => $r ) {
			$row_data = array(
				$r->reviewer_name,
				$r->contact_method,
				$r->date_sent ?? '',
				$r->review_posted ? 'Yes' : 'No',
			);
			for ( $u = 0; $u < $max_urls; $u++ ) {
				$row_data[] = $all_urls[ $i ][ $u ] ?? '';
			}
			$rows[] = $row_data;
		}

		if ( 'csv' === $format ) {
			$filename = 'arc-reviewers-' . $slug . '-' . $date . '.csv';
			header( 'Content-Type: text/csv; charset=utf-8' );
			header( 'Content-Disposition: attachment; filename="' . $filename . '"' );
			$out = fopen( 'php://output', 'w' );
			foreach ( $rows as $row ) {
				fputcsv( $out, $row );
			}
			fclose( $out ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fclose -- WP_Filesystem migration deferred to PC-FILESYS sprint; PHP native file ops are safe here (temp files in get_temp_dir(), never inside wp-content)
			exit;
		}

		// Default: XLSX.
		require_once MBS_INCLUDES_DIR . 'export/class-mbs-xlsx-builder.php';
		$filename  = 'arc-reviewers-' . $slug . '-' . $date . '.xlsx';
		$dest_path = get_temp_dir() . 'mbs-temp-import/arc-' . $post_id . '-' . time() . '.xlsx';
		wp_mkdir_p( dirname( $dest_path ) );

		$result = MBS_XLSX_Builder::build( $rows, $dest_path );
		if ( is_wp_error( $result ) ) {
			wp_send_json_error( $result->get_error_message(), 500 );
		}

		header( 'Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' );
		header( 'Content-Disposition: attachment; filename="' . $filename . '"' );
		header( 'Content-Length: ' . filesize( $dest_path ) );
		readfile( $dest_path ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_readfile -- WP_Filesystem migration deferred to PC-FILESYS sprint; PHP native file ops are safe here (temp files in get_temp_dir(), never inside wp-content)
		wp_delete_file( $dest_path );
		exit;
	}

	/** AJAX JSON export handler (F-027). Streams JSON file download. */
	public function ajax_export_json(): void {
		$post_id = absint( $_POST['post_id'] ?? 0 );
		$nonce   = sanitize_text_field( wp_unslash( $_POST['nonce'] ?? '' ) );

		if ( ! wp_verify_nonce( $nonce, 'mbs_export_arc_' . $post_id ) ) {
			wp_send_json_error( 'Invalid nonce', 403 );
		}
		if ( ! current_user_can( 'edit_post', $post_id ) ) {
			wp_send_json_error( 'Insufficient permissions', 403 );
		}
		if ( ! mbs_is_pro() ) {
			wp_send_json_error( 'PRO license required', 403 );
		}

		$reviewers         = MBS_Arc_Recipient_DAO::get_by_book( $post_id );
		$urls_by_recipient = MBS_Arc_Recipient_DAO::get_urls_for_book( $post_id ); // R2-004
		$slug              = get_post_field( 'post_name', $post_id ) ?: 'book-' . $post_id;
		$date              = gmdate( 'Y-m-d' );
		$filename          = 'arc-reviewers-' . $slug . '-' . $date . '.json';

		$payload = array();
		foreach ( $reviewers as $r ) {
			// R2-004: URLs from child table; legacy review_url fallback for pre-4.28 rows.
			$urls = $urls_by_recipient[ (int) $r->id ] ?? array();
			if ( empty( $urls ) && ! empty( $r->review_url ) ) {
				$urls = array( $r->review_url );
			}
			$payload[] = array(
				'reviewer_name'  => $r->reviewer_name,
				'contact_method' => $r->contact_method,
				'contact_type'   => $r->contact_type ?? null,
				'date_sent'      => $r->date_sent,
				'review_posted'  => (bool) $r->review_posted,
				'review_urls'    => $urls,
			);
		}

		header( 'Content-Type: application/json; charset=utf-8' );
		header( 'Content-Disposition: attachment; filename="' . $filename . '"' );
		echo wp_json_encode( $payload, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE );
		exit;
	}

	/**
	 * AJAX: Approve a pending ARC applicant.
	 * Verifies nonce + capability, checks tier limit, inserts into
	 * mbs_arc_recipients, updates applicant status, fires ESP subscribe.
	 */
	public function ajax_approve_applicant(): void {
		$id    = absint( $_POST['applicant_id'] ?? 0 );
		$nonce = sanitize_text_field( wp_unslash( $_POST['nonce'] ?? '' ) );

		if ( ! wp_verify_nonce( $nonce, 'mbs_arc_applicant_action_' . $id ) ) {
			wp_send_json_error( 'Invalid nonce', 403 );
		}
		if ( ! current_user_can( 'edit_posts' ) ) {
			wp_send_json_error( 'Insufficient permissions', 403 );
		}

		$row = MBS_Arc_Applicant_DAO::get_pending_by_id( $id );

		if ( ! $row ) {
			wp_send_json_error( 'Applicant not found or already processed', 404 );
		}

		$post_id = (int) $row['post_id'];
		if ( ! current_user_can( 'edit_post', $post_id ) ) {
			wp_send_json_error( array( 'message' => 'Insufficient permissions.' ), 403 );
		}
		$is_pub  = mbs_is_pub();
		$is_pro  = mbs_is_pro();
		$limit   = $is_pub ? PHP_INT_MAX : ( $is_pro ? 25 : 5 );

		// Tier limit check.
		$current_count = MBS_Arc_Recipient_DAO::count_by_book( $post_id );
		if ( $current_count >= $limit ) {
			wp_send_json_error( sprintf(
				/* translators: %s: upgrade URL */
				__( 'Reviewer limit reached. <a href="%s" target="_blank">Upgrade your plan</a> to add more reviewers.', 'my-book-showroom' ),
				esc_url( MBS_UPGRADE_URL )
			), 403 );
		}

		// Insert into recipients table.
		$inserted = MBS_Arc_Recipient_DAO::insert( array(
			'post_id'        => $post_id,
			'reviewer_name'  => $row['name'],
			'contact_method' => $row['email'],
			'contact_type'   => 'email',
		) );

		if ( ! $inserted ) {
			wp_send_json_error( 'Failed to add reviewer', 500 );
		}

		MBS_Arc_Applicant_DAO::set_status( $id, 'approved' );

		// F-027-6: Best-effort ESP subscribe on approval.
		$adapter = MBS_ESP_Registry::get_adapter();
		if ( null !== $adapter
			&& '1' === mbs_get_setting( 'email', 'arc_sync_enabled', '0' )
			&& '' !== mbs_get_setting( 'email', 'arc_list_id', '' ) ) {
			$adapter->subscribe(
				$row['email'],
				$row['name'],
				mbs_get_setting( 'email', 'arc_list_id', '' ),
				'1' === mbs_get_setting( 'email', 'double_optin', '0' )
			);
			// Best-effort: result discarded; approval already committed.
		}

		wp_send_json_success( array( 'applicant_id' => $id ) );
	}

	/**
	 * AJAX: Reject a pending ARC applicant.
	 * Sets status = 'rejected'. Row retained for audit trail.
	 */
	public function ajax_reject_applicant(): void {
		$id    = absint( $_POST['applicant_id'] ?? 0 );
		$nonce = sanitize_text_field( wp_unslash( $_POST['nonce'] ?? '' ) );

		if ( ! wp_verify_nonce( $nonce, 'mbs_arc_applicant_action_' . $id ) ) {
			wp_send_json_error( 'Invalid nonce', 403 );
		}
		if ( ! current_user_can( 'edit_posts' ) ) {
			wp_send_json_error( 'Insufficient permissions', 403 );
		}

		$row = MBS_Arc_Applicant_DAO::get_pending_by_id( $id );
		if ( ! $row ) {
			wp_send_json_error( 'Applicant not found or already processed', 404 );
		}
		if ( ! current_user_can( 'edit_post', (int) $row['post_id'] ) ) {
			wp_send_json_error( array( 'message' => 'Insufficient permissions.' ), 403 );
		}

		MBS_Arc_Applicant_DAO::set_status( $id, 'rejected' );

		wp_send_json_success( array( 'applicant_id' => $id ) );
	}
}
