<?php
/**
 * BitForgeNet Guest Tickets - Transactional email.
 *
 * @package BitForgeNet_Guest_Tickets
 * @since   1.0.0
 */

defined( 'ABSPATH' ) || exit;

// This file talks to the plugin's own tables. WordPress has no API for them --
// they are not posts and not meta -- so $wpdb is the only way in. The rows
// change on every booking and every scan, which makes an object cache wrong
// more often than it is right. Table names are built from $wpdb->prefix and
// never from a request, so interpolating one carries nothing to inject; every
// value goes through $wpdb->prepare(), and the few identifiers that cannot
// (ORDER BY columns) are matched against a whitelist first.
// phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery
// phpcs:disable WordPress.DB.DirectDatabaseQuery.NoCaching
// phpcs:disable WordPress.DB.DirectDatabaseQuery.SchemaChange
// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
// phpcs:disable PluginCheck.Security.DirectDB.UnescapedDBParameter


/**
 * Builds and sends the plugin's emails, and records every attempt.
 */
class BFNET_Mailer {

	/**
	 * Send the "reservation received, awaiting payment" email.
	 *
	 * @param int $order_id Order id.
	 * @return bool
	 */
	public static function send_pending( $order_id ) {
		if ( ! (int) BFNET_Settings::get( 'send_pending_email', 1 ) ) {
			return false;
		}

		$order = BFNET_Order::get( $order_id );
		if ( ! $order ) {
			return false;
		}

		$event     = BFNET_Event::get( $order->event_id );
		$organizer = $event ? BFNET_Organizer::get( $event->organizer_id ) : null;
		$attendees = BFNET_Order::attendees( $order_id );

		$whatsapp = $event && $event->whatsapp_number ? $event->whatsapp_number : (string) BFNET_Settings::get( 'whatsapp_number', '' );
		$whatsapp = $whatsapp ? $whatsapp : ( $organizer ? $organizer->whatsapp : '' );

		$instructions = $event && $event->payment_instructions
			? $event->payment_instructions
			: (string) BFNET_Settings::get( 'payment_instructions', '' );

		$rows = '';
		foreach ( $attendees as $attendee ) {
			$rows .= '<tr><td style="padding:8px 0;border-bottom:1px solid #EEE;font-size:15px;color:#222;">'
				. esc_html( BFNET_Util::full_name( $attendee->first_name, $attendee->last_name ) )
				. '</td></tr>';
		}

		$body = self::block_heading( __( 'We have your reservation', 'bitforgenet-guest-tickets' ) )
			. self::block_paragraph(
				sprintf(
					/* translators: %s: buyer first name. */
					__( 'Hello %s, thank you for reserving your place. Your seats are held for you — they are confirmed as soon as we receive your payment.', 'bitforgenet-guest-tickets' ),
					esc_html( $order->first_name )
				)
			)
			. self::block_event_card( $event, $order )
			. self::block_section( __( 'Guests on this reservation', 'bitforgenet-guest-tickets' ), '<table width="100%" cellpadding="0" cellspacing="0">' . $rows . '</table>' )
			. self::block_section(
				__( 'How to complete your payment', 'bitforgenet-guest-tickets' ),
				wpautop( wp_kses_post( $instructions ) )
			);

		if ( $whatsapp ) {
			$digits = preg_replace( '/\D/', '', $whatsapp );
			$link   = 'https://wa.me/' . $digits . '?text=' . rawurlencode(
				sprintf(
					/* translators: 1: order reference, 2: event title. */
					__( 'Hello, I would like to pay for reservation %1$s (%2$s).', 'bitforgenet-guest-tickets' ),
					$order->reference,
					$event ? $event->title : ''
				)
			);
			$body .= self::block_button( __( 'Contact us on WhatsApp', 'bitforgenet-guest-tickets' ), $link );
		}

		$subject = sprintf(
			/* translators: 1: event title, 2: order reference. */
			__( 'Reservation received — %1$s (%2$s)', 'bitforgenet-guest-tickets' ),
			$event ? $event->title : __( 'your event', 'bitforgenet-guest-tickets' ),
			$order->reference
		);

		return self::send( $order->email, $subject, self::wrap( $subject, $body, $organizer ), array(), $order_id, 0, (int) $order->event_id, 'order_pending' );
	}

	/**
	 * Send the "payment confirmed, here are your tickets" email.
	 *
	 * @param int   $order_id    Order id.
	 * @param array $attachments Absolute paths to attach.
	 * @param array $issued      Issued ticket map for the summary table.
	 * @param array $links       Signed per-guest download links.
	 * @return bool
	 */
	public static function send_tickets( $order_id, array $attachments = array(), array $issued = array(), array $links = array() ) {
		if ( ! (int) BFNET_Settings::get( 'send_paid_email', 1 ) ) {
			return false;
		}

		$order = BFNET_Order::get( $order_id );
		if ( ! $order ) {
			return false;
		}

		$event     = BFNET_Event::get( $order->event_id );
		$organizer = $event ? BFNET_Organizer::get( $event->organizer_id ) : null;

		$rows = '';
		foreach ( $issued as $entry ) {
			$ticket   = $entry['ticket'];
			$attendee = isset( $entry['attendee'] ) ? $entry['attendee'] : BFNET_Ticket::attendee( $ticket );
			$name     = $attendee ? BFNET_Util::full_name( $attendee->first_name, $attendee->last_name ) : '';

			$rows .= '<tr>'
				. '<td style="padding:10px 0;border-bottom:1px solid #EEE;font-size:15px;color:#222;">' . esc_html( $name ) . '</td>'
				. '<td style="padding:10px 0;border-bottom:1px solid #EEE;font-size:14px;color:#666;font-family:monospace;text-align:right;">' . esc_html( $ticket->serial ) . '</td>'
				. '</tr>';
		}

		if ( $attachments ) {
			/* translators: %s: buyer first name. */
			$intro = __( 'Hello %s, your payment has been received. Your tickets are attached to this email as PDF files.', 'bitforgenet-guest-tickets' );
		} else {
			/* translators: %s: buyer first name. */
			$intro = __( 'Hello %s, your payment has been received. Your tickets are ready — download each one using the buttons below.', 'bitforgenet-guest-tickets' );
		}

		$body = self::block_heading( __( 'Payment confirmed — here are your tickets', 'bitforgenet-guest-tickets' ) )
			. self::block_paragraph(
				sprintf(
					/* translators: %s: buyer first name. */
					$intro,
					esc_html( $order->first_name )
				)
			)
			. self::block_event_card( $event, $order )
			. self::block_section( __( 'Your tickets', 'bitforgenet-guest-tickets' ), '<table width="100%" cellpadding="0" cellspacing="0">' . $rows . '</table>' );

		// Download links always travel with the email. Mail servers strip attachments
		// far more often than anyone admits, and a guest at the door needs a way in.
		if ( $links ) {
			$link_rows = '';
			foreach ( $links as $link ) {
				$link_rows .= '<tr>'
					. '<td style="padding:9px 0;border-bottom:1px solid #EEE;font-size:15px;color:#222;">' . esc_html( $link['name'] ) . '</td>'
					. '<td style="padding:9px 0;border-bottom:1px solid #EEE;text-align:right;">'
					. '<a href="' . esc_url( $link['url'] ) . '" style="display:inline-block;background:#0B0B0B;color:#FFFFFF;text-decoration:none;padding:8px 15px;border-radius:7px;font-size:13.5px;font-weight:600;">'
					. esc_html__( 'Download ticket', 'bitforgenet-guest-tickets' ) . '</a></td>'
					. '</tr>';
			}

			$days = max( 1, (int) BFNET_Settings::get( 'download_link_days', 30 ) );

			$body .= self::block_section(
				__( 'Download each ticket', 'bitforgenet-guest-tickets' ),
				'<table width="100%" cellpadding="0" cellspacing="0">' . $link_rows . '</table>'
				. '<p style="margin:10px 0 0;font-size:12.5px;color:#8A8A8A;">'
				. esc_html(
					sprintf(
						/* translators: %d: number of days the links stay valid. */
						_n( 'These links stay valid for %d day.', 'These links stay valid for %d days.', $days, 'bitforgenet-guest-tickets' ),
						$days
					)
				)
				. '</p>'
			);
		}

		$body .= self::block_callout(
			__( 'Each guest has their own ticket and their own QR code. Every QR code is scanned once at the door, so please make sure each guest carries their own ticket — printed or on their phone.', 'bitforgenet-guest-tickets' )
		);

		if ( $event ) {
			$venue = BFNET_Venue::get( $event->venue_id );
			$map   = BFNET_Venue::map_url( $venue );
			if ( $map ) {
				$body .= self::block_button( __( 'Open the venue in Google Maps', 'bitforgenet-guest-tickets' ), $map );
			}
		}

		$subject = sprintf(
			/* translators: 1: event title, 2: order reference. */
			__( 'Your tickets — %1$s (%2$s)', 'bitforgenet-guest-tickets' ),
			$event ? $event->title : __( 'your event', 'bitforgenet-guest-tickets' ),
			$order->reference
		);

		return self::send( $order->email, $subject, self::wrap( $subject, $body, $organizer ), $attachments, $order_id, 0, (int) $order->event_id, 'order_tickets' );
	}

	/**
	 * Notify the team that a new reservation came in.
	 *
	 * @param int $order_id Order id.
	 * @return bool
	 */
	public static function notify_team( $order_id ) {
		$order = BFNET_Order::get( $order_id );
		if ( ! $order ) {
			return false;
		}

		$to = (string) BFNET_Settings::get( 'email_bcc', '' );
		$to = $to ? $to : get_option( 'admin_email' );
		if ( ! $to ) {
			return false;
		}

		$event = BFNET_Event::get( $order->event_id );

		$body = self::block_heading( __( 'New reservation awaiting payment', 'bitforgenet-guest-tickets' ) )
			. self::block_paragraph(
				sprintf(
					/* translators: 1: buyer name, 2: quantity, 3: event title. */
					__( '%1$s reserved %2$d place(s) for %3$s.', 'bitforgenet-guest-tickets' ),
					esc_html( BFNET_Util::full_name( $order->first_name, $order->last_name ) ),
					(int) $order->quantity,
					esc_html( $event ? $event->title : '' )
				)
			)
			. self::block_section(
				__( 'Contact', 'bitforgenet-guest-tickets' ),
				'<p style="margin:0;font-size:15px;color:#222;">' . esc_html( $order->email ) . '<br>' . esc_html( $order->phone ) . '</p>'
			)
			. self::block_button( __( 'Open in the dashboard', 'bitforgenet-guest-tickets' ), BFNET_Util::admin_url( 'bitforgenet-guest-tickets-orders', array( 'action' => 'view', 'order' => (int) $order_id ) ) );

		$subject = sprintf(
			/* translators: 1: order reference, 2: event title. */
			__( '[BitForgeNet Guest Tickets] New reservation %1$s — %2$s', 'bitforgenet-guest-tickets' ),
			$order->reference,
			$event ? $event->title : ''
		);

		return self::send( $to, $subject, self::wrap( $subject, $body, null ), array(), $order_id, 0, (int) $order->event_id, 'team_notification' );
	}

	/* --------------------------------------------------------------------- */
	/* Delivery                                                              */
	/* --------------------------------------------------------------------- */

	/**
	 * Send an email and log the attempt.
	 *
	 * @param string $to          Recipient.
	 * @param string $subject     Subject.
	 * @param string $html        HTML body.
	 * @param array  $attachments Attachment paths.
	 * @param int    $order_id    Related order.
	 * @param int    $ticket_id   Related ticket.
	 * @param int    $event_id    Related event.
	 * @param string $type        Log type.
	 * @return bool
	 */
	/**
	 * The name a guest should see the message arrive from.
	 *
	 * Preference goes to the event's own organizer, so an invitation to a
	 * an event arrives from the organizer's name rather than from whatever the
	 * website happens to be called.
	 *
	 * @param int $event_id Event the message concerns.
	 * @return string
	 */
	public static function sender_name( $event_id = 0 ) {
		if ( $event_id ) {
			$event = BFNET_Event::get( $event_id );
			if ( $event && ! empty( $event->organizer_id ) ) {
				$organizer = BFNET_Organizer::get( (int) $event->organizer_id );
				if ( $organizer && ! empty( $organizer->name ) ) {
					return (string) $organizer->name;
				}
			}
		}

		$name = trim( (string) BFNET_Settings::get( 'email_from_name', '' ) );

		return '' !== $name ? $name : (string) get_bloginfo( 'name' );
	}

	/**
	 * The address a guest reaches when they press reply.
	 *
	 * Reply-To is the one part of the identity a sending plugin leaves alone.
	 * Even where the From is rewritten out from under us, a guest who replies
	 * still reaches the organiser.
	 *
	 * @return string Empty when none is configured.
	 */
	public static function reply_to() {
		$email = trim( (string) BFNET_Settings::get( 'email_reply_to', '' ) );

		return ( $email && is_email( $email ) ) ? $email : '';
	}

	public static function send( $to, $subject, $html, $attachments = array(), $order_id = 0, $ticket_id = 0, $event_id = 0, $type = 'generic' ) {
		global $wpdb;

		$to = sanitize_email( $to );
		if ( ! $to ) {
			return false;
		}

		$from_name  = self::sender_name( $event_id );
		$from_email = (string) BFNET_Settings::get( 'email_from_address', '' );

		$headers = array( 'Content-Type: text/html; charset=UTF-8' );
		if ( $from_email && is_email( $from_email ) ) {
			$headers[] = sprintf( 'From: %s <%s>', $from_name, $from_email );
		}

		// Reply-To is the one part of the identity a sending plugin leaves alone.
		// Even where the From is rewritten out from under us, a guest who presses
		// reply still reaches the organiser.
		$reply_to = self::reply_to();
		if ( $reply_to ) {
			$headers[] = sprintf( 'Reply-To: %s <%s>', $from_name, $reply_to );
		}

		$bcc = (string) BFNET_Settings::get( 'email_bcc', '' );
		if ( $bcc && is_email( $bcc ) && 'team_notification' !== $type ) {
			$headers[] = 'Bcc: ' . $bcc;
		}

		$log_table = BFNET_Install::table( 'emails' );
		$wpdb->insert(
			$log_table,
			array(
				'order_id'   => (int) $order_id,
				'ticket_id'  => (int) $ticket_id,
				'event_id'   => (int) $event_id,
				'type'       => substr( sanitize_key( $type ), 0, 60 ),
				'recipient'  => $to,
				'subject'    => substr( $subject, 0, 250 ),
				'status'     => 'queued',
				'attempts'   => 1,
				'created_at' => current_time( 'mysql' ),
			)
		);
		$log_id = (int) $wpdb->insert_id;

		// A From: header is only a request. Plenty of sites run a plugin that
		// rewrites the sender globally on wp_mail_from_name, which is how a
		// ticket ended up arriving as "My Example Style Update". These filters
		// run last, and only for the duration of our own message.
		$force_name = static function () use ( $from_name ) {
			return $from_name;
		};
		$force_addr = static function ( $current ) use ( $from_email ) {
			return ( $from_email && is_email( $from_email ) ) ? $from_email : $current;
		};

		add_filter( 'wp_mail_from_name', $force_name, PHP_INT_MAX );
		add_filter( 'wp_mail_from', $force_addr, PHP_INT_MAX );

		$capture = static function ( $error ) use ( $wpdb, $log_table, $log_id ) {
			$wpdb->update(
				$log_table,
				array( 'status' => 'failed', 'error' => substr( $error->get_error_message(), 0, 500 ) ),
				array( 'id' => $log_id ),
				array( '%s', '%s' ),
				array( '%d' )
			);
		};
		add_action( 'wp_mail_failed', $capture );

		$files = array_values( array_filter( $attachments, 'is_readable' ) );

		/**
		 * Hand the message to a transport of its own, instead of wp_mail.
		 *
		 * This is the seam every alternative delivery route hangs from. A
		 * transport returns true when it has sent the message, a WP_Error when
		 * it tried and failed, and null — the default — to say "not mine", which
		 * leaves the message to wp_mail below.
		 *
		 * It exists so that delivery is a module rather than a branch. A build
		 * without the Google transport is a build that does not contain it, not
		 * one where an `if` is never true.
		 *
		 * @param null|true|WP_Error $result  Whether a transport took it.
		 * @param array              $message The message, ready to send.
		 */
		$result = apply_filters(
			'bfnet_mail_transport',
			null,
			array(
				'from_name'   => $from_name,
				'from_email'  => $from_email,
				'to'          => $to,
				'reply_to'    => $reply_to,
				'bcc'         => ( $bcc && is_email( $bcc ) && 'team_notification' !== $type ) ? $bcc : '',
				'subject'     => $subject,
				'html'        => $html,
				'attachments' => $files,
			)
		);

		if ( null === $result ) {
			// Nothing claimed it, so it goes the ordinary way — through whatever
			// SMTP the site already has. These two let a module step in front of
			// PHPMailer for the duration of our own message and no longer.
			do_action( 'bfnet_mail_sending' );

			$sent = wp_mail( $to, $subject, $html, $headers, $files );

			do_action( 'bfnet_mail_sent' );
		} else {
			$sent = ( true === $result );

			if ( ! $sent ) {
				$wpdb->update(
					$log_table,
					array( 'status' => 'failed', 'error' => substr( is_wp_error( $result ) ? $result->get_error_message() : 'unknown', 0, 500 ) ),
					array( 'id' => $log_id ),
					array( '%s', '%s' ),
					array( '%d' )
				);
			}
		}

		remove_action( 'wp_mail_failed', $capture );
		remove_filter( 'wp_mail_from_name', $force_name, PHP_INT_MAX );
		remove_filter( 'wp_mail_from', $force_addr, PHP_INT_MAX );

		if ( $sent ) {
			$wpdb->update(
				$log_table,
				array( 'status' => 'sent', 'sent_at' => current_time( 'mysql' ) ),
				array( 'id' => $log_id ),
				array( '%s', '%s' ),
				array( '%d' )
			);
		}

		return (bool) $sent;
	}

	/* --------------------------------------------------------------------- */
	/* HTML building blocks                                                  */
	/* --------------------------------------------------------------------- */

	/**
	 * Wrap body content in the branded email shell.
	 *
	 * @param string      $title     Preheader / title.
	 * @param string      $content   Inner HTML.
	 * @param object|null $organizer Organizer for branding.
	 * @return string
	 */
	public static function wrap( $title, $content, $organizer = null ) {
		$primary = $organizer && ! empty( $organizer->brand_primary ) ? $organizer->brand_primary : (string) BFNET_Settings::get( 'brand_primary', '#C9A227' );
		$dark    = $organizer && ! empty( $organizer->brand_secondary ) ? $organizer->brand_secondary : (string) BFNET_Settings::get( 'brand_secondary', '#0B0B0B' );
		$logo    = BFNET_Organizer::logo_url( $organizer );
		$name    = $organizer ? $organizer->name : (string) BFNET_Settings::get( 'brand_name', get_bloginfo( 'name' ) );
		$footer  = (string) BFNET_Settings::get( 'email_footer', '' );

		ob_start();
		?>
<!DOCTYPE html>
<html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
<title><?php echo esc_html( $title ); ?></title></head>
<body style="margin:0;padding:0;background:#F4F4F6;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Helvetica,Arial,sans-serif;">
<div style="display:none;max-height:0;overflow:hidden;opacity:0;"><?php echo esc_html( $title ); ?></div>
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="background:#F4F4F6;padding:28px 12px;">
<tr><td align="center">
	<table role="presentation" width="600" cellpadding="0" cellspacing="0" style="max-width:600px;width:100%;background:#FFFFFF;border-radius:14px;overflow:hidden;box-shadow:0 2px 12px rgba(0,0,0,.06);">
		<tr><td style="background:<?php echo esc_attr( $dark ); ?>;padding:30px 24px;text-align:center;">
			<img src="<?php echo esc_url( $logo ); ?>" alt="<?php echo esc_attr( $name ); ?>" width="86" style="width:86px;height:auto;display:inline-block;">
			<div style="margin-top:12px;color:<?php echo esc_attr( $primary ); ?>;font-size:13px;letter-spacing:4px;text-transform:uppercase;"><?php echo esc_html( $name ); ?></div>
		</td></tr>
		<tr><td style="padding:30px 30px 8px;"><?php echo $content; // phpcs:ignore WordPress.Security.EscapeOutput ?></td></tr>
		<tr><td style="padding:20px 30px 30px;border-top:1px solid #EEE;color:#8A8A8A;font-size:12px;line-height:1.6;">
			<?php echo esc_html( $footer ); ?><br>
			<a href="<?php echo esc_url( home_url( '/' ) ); ?>" style="color:<?php echo esc_attr( $primary ); ?>;text-decoration:none;"><?php echo esc_html( wp_parse_url( home_url(), PHP_URL_HOST ) ); ?></a>
		</td></tr>
	</table>
</td></tr></table>
</body></html>
		<?php
		return (string) ob_get_clean();
	}

	/**
	 * Heading block.
	 *
	 * @param string $text Heading text.
	 * @return string
	 */
	private static function block_heading( $text ) {
		return '<h1 style="margin:0 0 16px;font-size:23px;line-height:1.3;color:#111;font-weight:600;">' . esc_html( $text ) . '</h1>';
	}

	/**
	 * Paragraph block.
	 *
	 * @param string $text Paragraph HTML.
	 * @return string
	 */
	private static function block_paragraph( $text ) {
		return '<p style="margin:0 0 18px;font-size:15px;line-height:1.65;color:#3A3A3A;">' . $text . '</p>';
	}

	/**
	 * Titled section block.
	 *
	 * @param string $title Section title.
	 * @param string $html  Section body.
	 * @return string
	 */
	private static function block_section( $title, $html ) {
		return '<div style="margin:22px 0 6px;font-size:12px;letter-spacing:2px;text-transform:uppercase;color:#9A9A9A;">' . esc_html( $title ) . '</div>' . $html;
	}

	/**
	 * Highlighted note block.
	 *
	 * @param string $text Note text.
	 * @return string
	 */
	private static function block_callout( $text ) {
		$primary = (string) BFNET_Settings::get( 'brand_primary', '#C9A227' );
		return '<div style="margin:22px 0;padding:14px 16px;background:#FBF7EA;border-left:3px solid ' . esc_attr( $primary ) . ';font-size:14px;line-height:1.6;color:#4A4A4A;">' . esc_html( $text ) . '</div>';
	}

	/**
	 * Call-to-action button.
	 *
	 * @param string $label Button label.
	 * @param string $url   Target URL.
	 * @return string
	 */
	private static function block_button( $label, $url ) {
		$dark = (string) BFNET_Settings::get( 'brand_secondary', '#0B0B0B' );
		return '<div style="margin:24px 0;"><a href="' . esc_url( $url ) . '" style="display:inline-block;background:' . esc_attr( $dark ) . ';color:#FFFFFF;text-decoration:none;padding:13px 26px;border-radius:8px;font-size:15px;font-weight:600;">' . esc_html( $label ) . '</a></div>';
	}

	/**
	 * Event summary card.
	 *
	 * @param object|null $event Event row.
	 * @param object      $order Order row.
	 * @return string
	 */
	private static function block_event_card( $event, $order ) {
		if ( ! $event ) {
			return '';
		}

		$venue = BFNET_Venue::get( $event->venue_id );

		$lines = array(
			array( __( 'Event', 'bitforgenet-guest-tickets' ), $event->title ),
			array( __( 'Date', 'bitforgenet-guest-tickets' ), BFNET_Util::date( $event->starts_at, 'l j F Y — H:i' ) ),
		);

		if ( $venue ) {
			$lines[] = array( __( 'Venue', 'bitforgenet-guest-tickets' ), $venue->name . ( $venue->city ? ', ' . $venue->city : '' ) );
		}

		$lines[] = array( __( 'Reference', 'bitforgenet-guest-tickets' ), $order->reference );
		$lines[] = array( __( 'Guests', 'bitforgenet-guest-tickets' ), (string) (int) $order->quantity );
		$lines[] = array( __( 'Total', 'bitforgenet-guest-tickets' ), BFNET_Util::money( $order->total_cents, $order->currency ) );

		$rows = '';
		foreach ( $lines as $line ) {
			$rows .= '<tr>'
				. '<td style="padding:7px 0;font-size:13px;color:#8A8A8A;width:38%;">' . esc_html( $line[0] ) . '</td>'
				. '<td style="padding:7px 0;font-size:15px;color:#161616;font-weight:500;">' . esc_html( $line[1] ) . '</td>'
				. '</tr>';
		}

		return '<table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="margin:18px 0;background:#FAFAFB;border:1px solid #ECECEF;border-radius:10px;padding:6px 16px;">' . $rows . '</table>';
	}
}
