<?php
/**
 * Data Export
 *
 * Handles CSV downloads for donors, donations, and ambassadors.
 *
 * @package AmbassadorHub\Admin\Export
 */

namespace AmbassadorHub\Admin\Export;

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

use AmbassadorHub\Accounts\Roles;
use AmbassadorHub\Consent\ConsentModule;
use AmbassadorHub\CustomFields\CustomFieldsManager;
use AmbassadorHub\Donations\DonationsModule;
use AmbassadorHub\Donors\DonorsModule;



/**
 * CSV export handler.
 */
final class Export {

	/**
	 * Register admin_post action hooks.
	 *
	 * @return void
	 */
	public static function init(): void {
		add_action( 'admin_post_ambh_export_donors', array( self::class, 'export_donors' ) );
		add_action( 'admin_post_ambh_export_donations', array( self::class, 'export_donations' ) );
		add_action( 'admin_post_ambh_export_ambassadors', array( self::class, 'export_ambassadors' ) );
		add_action( 'admin_post_ambh_export_consent_records', array( self::class, 'export_consent_records' ) );
	}

	// -------------------------------------------------------------------------
	// Export handlers
	// -------------------------------------------------------------------------

	/**
	 * Stream a CSV of all donors.
	 *
	 * @return void
	 */
	public static function export_donors(): void {
		check_admin_referer( 'ambh_export_donors', 'ambh_export_donors_nonce' );
		self::check_permission();

		global $wpdb;
		$table = esc_sql( DonorsModule::get_table_name() );
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- admin export action; $table is from $wpdb->prefix (safe); no cache needed.
		$rows = $wpdb->get_results( "SELECT * FROM {$table} ORDER BY id ASC", ARRAY_A );

		$columns = array(
			'id'           => __( 'ID', 'ambassadorhub' ),
			'first_name'   => __( 'First Name', 'ambassadorhub' ),
			'last_name'    => __( 'Last Name', 'ambassadorhub' ),
			'name'         => __( 'Full Name', 'ambassadorhub' ),
			'email'        => __( 'Email', 'ambassadorhub' ),
			'street'       => __( 'Street', 'ambassadorhub' ),
			'city'         => __( 'City', 'ambassadorhub' ),
			'postal_code'  => __( 'Postal Code', 'ambassadorhub' ),
			'state'        => __( 'State/Province', 'ambassadorhub' ),
			'country'      => __( 'Country', 'ambassadorhub' ),
			'is_anonymous' => __( 'Anonymous', 'ambassadorhub' ),
			'created_at'   => __( 'Created At', 'ambassadorhub' ),
			'updated_at'   => __( 'Updated At', 'ambassadorhub' ),
		);

		self::stream_csv( 'donors', $columns, $rows );
	}

	/**
	 * Stream a CSV of all donations.
	 *
	 * @return void
	 */
	public static function export_donations(): void {
		check_admin_referer( 'ambh_export_donations', 'ambh_export_donations_nonce' );
		self::check_permission();

		global $wpdb;
		$table        = esc_sql( DonationsModule::get_table_name() );
		$donors_table = esc_sql( DonorsModule::get_table_name() );
		// phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- admin export action; tables are from $wpdb->prefix (safe); no cache needed. Disabled (not ignored) because the interpolated identifiers span multiple lines inside this query string.
		$rows = $wpdb->get_results(
			"SELECT d.*,
			        dn.first_name  AS donor_first_name,
			        dn.last_name   AS donor_last_name,
			        dn.name        AS donor_name,
			        dn.email       AS donor_email,
			        dn.street      AS donor_street,
			        dn.city        AS donor_city,
			        dn.postal_code AS donor_postal_code,
			        dn.state       AS donor_state,
			        dn.country     AS donor_country
			 FROM {$table} d
			 LEFT JOIN {$donors_table} dn ON d.donor_id = dn.id
			 ORDER BY d.id ASC",
			ARRAY_A
		);
		// phpcs:enable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared

		$columns = array(
			'id'                => __( 'ID', 'ambassadorhub' ),
			'fundraiser_id'     => __( 'Fundraiser ID', 'ambassadorhub' ),
			'donor_id'          => __( 'Donor ID', 'ambassadorhub' ),
			'donor_first_name'  => __( 'First Name', 'ambassadorhub' ),
			'donor_last_name'   => __( 'Last Name', 'ambassadorhub' ),
			'donor_name'        => __( 'Full Name', 'ambassadorhub' ),
			'donor_email'       => __( 'Email', 'ambassadorhub' ),
			'donor_street'      => __( 'Street', 'ambassadorhub' ),
			'donor_city'        => __( 'City', 'ambassadorhub' ),
			'donor_postal_code' => __( 'Postal Code', 'ambassadorhub' ),
			'donor_state'       => __( 'State/Province', 'ambassadorhub' ),
			'donor_country'     => __( 'Country', 'ambassadorhub' ),
			'amount'            => __( 'Amount', 'ambassadorhub' ),
			'currency'          => __( 'Currency', 'ambassadorhub' ),
			'status'            => __( 'Status', 'ambassadorhub' ),
			'transaction_id'    => __( 'Transaction ID', 'ambassadorhub' ),
			'message'           => __( 'Message', 'ambassadorhub' ),
			'notes'             => __( 'Notes', 'ambassadorhub' ),
			'is_anonymous'      => __( 'Anonymous', 'ambassadorhub' ),
			'created_at'        => __( 'Created At', 'ambassadorhub' ),
			'updated_at'        => __( 'Updated At', 'ambassadorhub' ),
		);

		// Append a column for each configured donation custom field (Premium).
		$cf_fields = class_exists( CustomFieldsManager::class ) ? CustomFieldsManager::get_fields( 'donation' ) : array();
		foreach ( $cf_fields as $field ) {
			$columns[ 'cf_' . $field['id'] ] = $field['label'];
		}

		// Enrich each row with custom field values.
		if ( ! empty( $cf_fields ) ) {
			foreach ( $rows as &$row ) {
				$meta = CustomFieldsManager::get_all_donation_meta( (int) $row['id'] );
				foreach ( $cf_fields as $field ) {
					$meta_key                    = CustomFieldsManager::get_meta_key( $field['id'] );
					$row[ 'cf_' . $field['id'] ] = CustomFieldsManager::format_value_for_export( $field, $meta[ $meta_key ] ?? '' );
				}
			}
			unset( $row );
		}

		self::stream_csv( 'donations', $columns, $rows );
	}

	/**
	 * Stream a CSV of all ambassadors (users with the ambassador role).
	 *
	 * @return void
	 */
	public static function export_ambassadors(): void {
		check_admin_referer( 'ambh_export_ambassadors', 'ambh_export_ambassadors_nonce' );
		self::check_permission();

		$users = get_users(
			array(
				'role'   => Roles::ROLE_AMBASSADOR,
				'number' => -1,
				'fields' => 'all',
			)
		);

		$columns = array(
			'ID'               => __( 'ID', 'ambassadorhub' ),
			'user_login'       => __( 'Username', 'ambassadorhub' ),
			'user_email'       => __( 'Email', 'ambassadorhub' ),
			'display_name'     => __( 'Display Name', 'ambassadorhub' ),
			'first_name'       => __( 'First Name', 'ambassadorhub' ),
			'last_name'        => __( 'Last Name', 'ambassadorhub' ),
			'ambh_street'      => __( 'Street', 'ambassadorhub' ),
			'ambh_city'        => __( 'City', 'ambassadorhub' ),
			'ambh_postal_code' => __( 'Postal Code', 'ambassadorhub' ),
			'ambh_country'     => __( 'Country', 'ambassadorhub' ),
			'user_registered'  => __( 'Registered At', 'ambassadorhub' ),
		);

		// Append a column for each configured signup custom field (Premium).
		$cf_fields = class_exists( CustomFieldsManager::class ) ? CustomFieldsManager::get_fields( 'signup' ) : array();
		foreach ( $cf_fields as $field ) {
			$columns[ 'cf_' . $field['id'] ] = $field['label'];
		}

		$rows = array();
		foreach ( $users as $user ) {
			$row = array(
				'ID'               => $user->ID,
				'user_login'       => $user->user_login,
				'user_email'       => $user->user_email,
				'display_name'     => $user->display_name,
				'first_name'       => $user->first_name,
				'last_name'        => $user->last_name,
				'ambh_street'      => get_user_meta( $user->ID, 'ambh_street', true ),
				'ambh_city'        => get_user_meta( $user->ID, 'ambh_city', true ),
				'ambh_postal_code' => get_user_meta( $user->ID, 'ambh_postal_code', true ),
				'ambh_country'     => get_user_meta( $user->ID, 'ambh_country', true ),
				'user_registered'  => $user->user_registered,
			);

			foreach ( $cf_fields as $field ) {
				$meta_key                    = CustomFieldsManager::get_meta_key( $field['id'] );
				$raw                         = get_user_meta( $user->ID, $meta_key, true );
				$row[ 'cf_' . $field['id'] ] = CustomFieldsManager::format_value_for_export( $field, $raw );
			}

			$rows[] = $row;
		}

		self::stream_csv( 'ambassadors_', $columns, $rows );
	}

	/**
	 * Stream a CSV of all consent records.
	 *
	 * @return void
	 */
	public static function export_consent_records(): void {
		check_admin_referer( 'ambh_export_consent_records', 'ambh_export_consent_records_nonce' );
		self::check_permission();

		global $wpdb;
		$table = esc_sql( $wpdb->prefix . 'ambh_consent_records' );
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- admin export action; $table is from $wpdb->prefix (safe); no cache needed.
		$rows = $wpdb->get_results( "SELECT * FROM {$table} ORDER BY id ASC", ARRAY_A );

		$columns = array(
			'id'           => __( 'ID', 'ambassadorhub' ),
			'subject_type' => __( 'Subject Type', 'ambassadorhub' ),
			'subject_id'   => __( 'Subject ID', 'ambassadorhub' ),
			'email'        => __( 'Email', 'ambassadorhub' ),
			'purpose_slug' => __( 'Purpose', 'ambassadorhub' ),
			'channel'      => __( 'Channel', 'ambassadorhub' ),
			'consent_text' => __( 'Consent Text', 'ambassadorhub' ),
			'scope_type'   => __( 'Scope Type', 'ambassadorhub' ),
			'scope_id'     => __( 'Scope ID', 'ambassadorhub' ),
			'granted'      => __( 'Granted', 'ambassadorhub' ),
			'source'       => __( 'Source', 'ambassadorhub' ),
			'consented_at' => __( 'Consented At', 'ambassadorhub' ),
			'withdrawn_at' => __( 'Withdrawn At', 'ambassadorhub' ),
			'withdrawn_by' => __( 'Withdrawn By', 'ambassadorhub' ),
		);

		// Enrich rows with resolved email address.
		foreach ( $rows as &$row ) {
			$row['email'] = ConsentModule::get_subject_email( $row['subject_type'], (int) $row['subject_id'] ) ?? '';
		}
		unset( $row );

		self::stream_csv( 'consent-records', $columns, $rows );
	}

	// -------------------------------------------------------------------------
	// Helpers
	// -------------------------------------------------------------------------

	/**
	 * Check that the current user has the required capability.
	 *
	 * @return void
	 */
	private static function check_permission(): void {
		if ( ! current_user_can( 'ambh_manage_hub' ) ) {
			wp_die( esc_html__( 'You do not have permission to perform this action.', 'ambassadorhub' ) );
		}
	}

	/**
	 * Send CSV headers and stream row data, then exit.
	 *
	 * @param string   $name    File name prefix (no extension).
	 * @param string[] $columns Map of data_key => column header label.
	 * @param array[]  $rows    Array of associative row arrays.
	 * @return void
	 */
	private static function stream_csv( string $name, array $columns, array $rows ): void {
		$filename = sanitize_file_name( $name . '-' . gmdate( 'Y-m-d' ) . '.csv' );

		// Prevent caching and force download.
		nocache_headers();

		header( 'Content-Type: text/csv; charset=UTF-8' );
		header( 'Content-Disposition: attachment; filename="' . $filename . '"' );

		echo "\xEF\xBB\xBF"; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- UTF-8 BOM for Excel

		$output = fopen( 'php://output', 'w' ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fopen

		// Header row.
		fputcsv( $output, array_values( $columns ) );

		// Data rows.
		foreach ( $rows as $row ) {
			$line = array();
			foreach ( array_keys( $columns ) as $key ) {
				$line[] = $row[ $key ] ?? '';
			}
			fputcsv( $output, $line );
		}

		fclose( $output ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fclose
		exit;
	}
}
