<?php

abstract class WPFS_Transactions_Table extends WPFS_List_Table {
	use MM_WPFS_Logger_AddOn;
	use MM_WPFS_StaticContext_AddOn;

	const ITEMS_PER_PAGE = 10;

	/** @var MM_WPFS_Database */
	protected $db = null;

	/** @var MM_WPFS_Options */
	protected $options = null;

	/** @var array */
	protected $formDisplayNameCache;

	public function __construct( $loggerService, $args ) {
		parent::__construct( $args );

		$this->initLogger( $loggerService, MM_WPFS_LoggerService::MODULE_ADMIN );
		$this->options = new MM_WPFS_Options();
		$this->db = new MM_WPFS_Database();

		$this->initStaticContext();

		$this->initFormDisplayNameCache();
	}

	abstract protected function getForms(): array;

	protected function initFormDisplayNameCache() {
		$this->formDisplayNameCache = [];

		$forms = $this->getForms();
		foreach ( $forms as $form ) {
			$this->formDisplayNameCache[ $form->name ] = $form->displayName;
		}
	}

	/**
	 * @throws Exception
	 */
	public function prepare_items() {
		$query = $this->getQuery();
		$numItems = $this->db->runQuery( $query );

		$this->configurePagination( $numItems );
		$this->addPageOffsetToQuery( $query );

		$this->configureColumnHeaders();

		$this->items = $this->db->getResults( $query );
	}

	/**
	 * @return array
	 */
	protected function getOrderParameters(): array {
		$orderByParam = isset( $_REQUEST[ MM_WPFS_Admin_Menu::PARAM_NAME_ORDER_BY ] ) ? $_REQUEST[ MM_WPFS_Admin_Menu::PARAM_NAME_ORDER_BY ] : null;
		$orderBy      = 'created';
		
		if ( is_string( $orderByParam ) && ! empty( $orderByParam ) ) {
			$orderBy = trim( sanitize_sql_orderby( $orderByParam ) );
		}
		$order = ! empty( $_REQUEST[ MM_WPFS_Admin_Menu::PARAM_NAME_ORDER ] ) ? trim( $_REQUEST[ MM_WPFS_Admin_Menu::PARAM_NAME_ORDER ] ) : 'desc';

		if ( ! in_array( $order, [ 'desc', 'asc' ], true ) ) {
			$order = 'desc';
		}

		return [ $orderBy, $order ];
	}

	protected function addOrderByStatementToQuery( &$query ) {
		list( $orderBy, $order ) = $this->getOrderParameters();

		if ( ! empty( $orderBy ) && ! empty( $order ) ) {
			$query .= ' ORDER BY ' . $orderBy . ' ' . $order;
		}
	}

	/**
	 * @param $whereStatement
	 * @param $condition
	 */
	protected function extendWhereStatement( &$whereStatement, $condition ) {
		if ( ! isset( $whereStatement ) ) {
			$whereStatement = ' WHERE ';
		} else {
			$whereStatement .= ' AND ';
		}

		$whereStatement .= $condition;
	}

	/**
	 * @param $numItems
	 */
	protected function configurePagination( $numItems ) {
		$totalPages = ceil( $numItems / self::ITEMS_PER_PAGE );

		$this->set_pagination_args( [
			"total_items" => $numItems,
			"total_pages" => $totalPages,
			"per_page" => self::ITEMS_PER_PAGE,
		] );
	}

	protected function addPageOffsetToQuery( &$query ) {
		$currentPage = $this->get_pagenum();
		if ( ! empty( $currentPage ) ) {
			$offset = ( $currentPage - 1 ) * self::ITEMS_PER_PAGE;
			$query .= ' LIMIT ' . (int) $offset . ',' . (int) self::ITEMS_PER_PAGE;
		}
	}

	/**
	 *
	 */
	protected function configureColumnHeaders() {
		$columns = $this->get_columns();
		$hidden = [];
		$sortable = $this->get_sortable_columns();

		$this->_column_headers = [ $columns, $hidden, $sortable ];
	}

	protected function createCompositeColumnLabel( $column1, $column2 ) {
		return "{$column1} / {$column2}";
	}

	public function display() {
		?>
		<table class="<?php echo implode( ' ', $this->get_table_classes() ); ?>">
			<thead>
				<?php $this->print_column_headers(); ?>
			</thead>

			<tbody id="the-list">
				<?php $this->display_rows_or_placeholder(); ?>
			</tbody>
		</table>
		<?php
		$this->pagination( 'bottom' );
	}

	/**
	 * @param string $which
	 */
	protected function pagination( $which ) {
		if ( empty( $this->_pagination_args ) ) {
			return;
		}
		if ( $this->_pagination_args['total_items'] == 0 ) {
			return;
		}

		$removableQueryArgs = wp_removable_query_args();
		$currentUrl = set_url_scheme( 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] );
		$currentUrl = remove_query_arg( $removableQueryArgs, $currentUrl );

		$totalItems = $this->_pagination_args['total_items'];
		$totalPages = $this->_pagination_args['total_pages'];
		$currentPage = $this->get_pagenum();

		$firstItem = ( $currentPage - 1 ) * self::ITEMS_PER_PAGE + 1;
		$lastItem = $firstItem + self::ITEMS_PER_PAGE - 1 < $totalItems ? $firstItem + self::ITEMS_PER_PAGE - 1 : $totalItems;

		$disableFirst = $disableLast = $disablePrev = $disableNext = false;

		if ( $currentPage == 1 ) {
			$disableFirst = true;
			$disablePrev = true;
		}
		if ( $currentPage == 2 ) {
			$disableFirst = true;
		}
		if ( $currentPage == $totalPages ) {
			$disableLast = true;
			$disableNext = true;
		}
		if ( $currentPage == $totalPages - 1 ) {
			$disableLast = true;
		}

		if ( 1 === $totalItems ) {
			$showingLabel =
			/* translators: Paging label indicating which items are displayed when there is only one item
			 */
				__( 'Showing one item',
					'wp-full-stripe-free' );
		} else {
			$showingLabel = sprintf(
			/* translators: Paging label indicating which items are displayed
			 * p1: First item displayed
			 * p2: Last item displayed
			 * p3: Total number of items
			 */
				_n( 'Showing %1$d-%2$d of %3$d item',
					'Showing %1$d-%2$d of %3$d items',
					$totalItems,
					'wp-full-stripe-free' ),
				$firstItem,
				$lastItem,
				$totalItems
			);
		}
		$output = "<div class='wpfs-data-table-foot'>\n";
		$output .= "  <div class='wpfs-data-table-foot__show'>{$showingLabel}</div>\n";
		$output .= "  <div class='wpfs-data-table-foot__pagination'>\n";
		$output .= "    <div class='wpfs-pagination'>\n";
		$output .= "      <ul class='wpfs-pagination__list'>\n";

		$output .= "        <li class='wpfs-pagination__item'>\n";
		if ( $disableFirst ) {
			$output .= "          ←←";
		} else {
			$firstPageUrl = esc_url( remove_query_arg( 'paged', $currentUrl ) );
			$firstPageTitle =
				/* translators: Label of the button showing the first page of the list */
				__( 'First page', 'wp-full-stripe-free' );
			$output .= "          <a class='wpfs-pagination__link' href='{$firstPageUrl}'>←←</a>";
		}
		$output .= "        </li>\n";

		$output .= "        <li class='wpfs-pagination__item'>\n";
		if ( $disablePrev ) {
			$output .= "          ←";
		} else {
			$prevPageUrl = esc_url( add_query_arg( 'paged', max( 1, $currentPage - 1 ), $currentUrl ) );
			$prevPageTitle =
				/* translators: Label of the button showing the previous page of the list */
				__( 'Previous page', 'wp-full-stripe-free' );
			$output .= "          <a class='wpfs-pagination__link' href='{$prevPageUrl}'>←</a>";
		}
		$output .= "        </li>\n";

		$currentPageTitle =
			/* translators: Label of the button showing the current page of the list */
			__( 'Current Page', 'wp-full-stripe-free' );
		$output .= "        <li class='wpfs-pagination__item wpfs-pagination__item--active'>\n";
		$output .= "          {$currentPage}\n";
		$output .= "        </li>\n";

		$output .= "        <li class='wpfs-pagination__item'>\n";
		if ( $disableNext ) {
			$output .= "          →";
		} else {
			$nextPageUrl = esc_url( add_query_arg( 'paged', min( $totalPages, $currentPage + 1 ), $currentUrl ) );
			$nextPageTitle =
				/* translators: Label of the button showing the next page of the list */
				__( 'Next page', 'wp-full-stripe-free' );
			$output .= "          <a class='wpfs-pagination__link' href='{$nextPageUrl}'>→</a>";
		}
		$output .= "        </li>\n";

		$output .= "        <li class='wpfs-pagination__item'>\n";
		if ( $disableLast ) {
			$output .= "          →→";
		} else {
			$lastPageUrl = esc_url( add_query_arg( 'paged', $totalPages, $currentUrl ) );
			$lastPageTitle =
				/* translators: Label of the button showing the last page of the list */
				__( 'Last page', 'wp-full-stripe-free' );
			$output .= "          <a class='wpfs-pagination__link' href='{$lastPageUrl}'>→→</a>";
		}
		$output .= "        </li>\n";

		$output .= "      </ul>\n";
		$output .= "    </div>\n";
		$output .= "  </div>\n";
		$output .= "</div>\n";

		$this->__set( '_pagination', $output );

		echo $output;
	}

	/**
	 * @param $name
	 *
	 * @return string
	 */
	protected function lookupFormDisplayName( $name ) {
		return array_key_exists( $name, $this->formDisplayNameCache ) ? $this->formDisplayNameCache[ $name ] : $name;
	}

	/**
	 * @param $transaction
	 *
	 * @return string
	 */
	protected function renderPaymentMethodCell( $transaction ): string {
		$class = 'wpfs-credit-card--generic';
		if ( isset( $transaction->payment_method ) ) {
			switch ( $transaction->payment_method ) {
				case 'alipay':
					$class = 'wpfs-alipay';
					break;
				case 'amazon_pay':
					$class = 'wpfs-amazonpay';
					break;
				case 'bancontact':
					$class = 'wpfs-bancontact';
					break;
				case 'blik':
					$class = 'wpfs-blik';
					break;
				case 'cashapp':
					$class = 'wpfs-cashapp';
					break;
				case 'eps':
					$class = 'wpfs-eps';
					break;
				case 'grabpay':
					$class = 'wpfs-grabpay';
					break;
				case 'ideal':
					$class = 'wpfs-ideal';
					break;
				case 'klarna':
					$class = 'wpfs-klarna';
					break;
				case 'mobilepay':
					$class = 'wpfs-mobilepay';
					break;
				case 'p24':
					$class = 'wpfs-przelewy24';
					break;
				case 'revolut_pay':
					$class = 'wpfs-revolut_pay';
					break;
				case 'twint':
					$class = 'wpfs-twint';
					break;
				case 'wechat_pay':
					$class = 'wpfs-wechatpay';
					break;
				default:
					// default is card
					break;
			}
		}
		$html = "
        <td class='wpfs-data-table__td wpfs-data-table__td--w40'>
            <div class='wpfs-credit-card " . $class . "'></div>
        </td>";

		return $html;
	}

	/**
	 * @return string[]
	 */
	protected function get_table_classes() {
		return [ 'wpfs-data-table' ];
	}

}

class WPFS_Subscriptions_Table extends WPFS_Transactions_Table {
	const COLUMN_PAYMENT_METHOD = 'paymentMethod';
	const COLUMN_ID_DATE = 'idDate';
	const COLUMN_SUBSCRIBER_FORM = 'subscriberForm';
	const COLUMN_AMOUNT_FREQUENCY = 'amountFrequency';
	const COLUMN_STATUS_MODE = 'statusMode';
	const COLUMN_ACTIONS = 'actions';

	/** @var $stripeDev MM_WPFS_Stripe */
	private $stripeDev = null;
	/** @var $stripeLive MM_WPFS_Stripe */
	private $stripeLive = null;

	/**
	 * @param $loggerService MM_WPFS_LoggerService
	 */
	public function __construct( $loggerService ) {
		parent::__construct( $loggerService, [
			'singular' =>
				/* translators: Singular version of the word 'subscription', used on the subscription list page */
				__( 'Subscription', 'wp-full-stripe-free' ),
			'plural' =>
				/* translators: Plural version of the word 'subscription', used on the subscription list page */
				__( 'Subscriptions', 'wp-full-stripe-free' ),
			'ajax' => false
		] );
	}

	/**
	 * @return array
	 */
	protected function getForms(): array {
		return $this->db->getSubscriptionFormNames();
	}

	/**
	 * @return array
	 */
	protected function getFilterParameters(): array {
		$searchText = ! empty( $_REQUEST[ MM_WPFS_Admin_Menu::PARAM_NAME_SUBSCRIPTIONS_TEXT_FILTER ] ) ? trim( $_REQUEST[ MM_WPFS_Admin_Menu::PARAM_NAME_SUBSCRIPTIONS_TEXT_FILTER ] ) : null;
		$status = ! empty( $_REQUEST[ MM_WPFS_Admin_Menu::PARAM_NAME_SUBSCRIPTIONS_STATUS_FILTER ] ) ? trim( $_REQUEST[ MM_WPFS_Admin_Menu::PARAM_NAME_SUBSCRIPTIONS_STATUS_FILTER ] ) : null;
		$mode = ! empty( $_REQUEST[ MM_WPFS_Admin_Menu::PARAM_NAME_SUBSCRIPTIONS_MODE_FILTER ] ) ? trim( $_REQUEST[ MM_WPFS_Admin_Menu::PARAM_NAME_SUBSCRIPTIONS_MODE_FILTER ] ) : null;

		return [ $searchText, $status, $mode ];
	}

	protected function getQuery() {
		$dbPrefix = $this->db->getDatabasePrefix();
		$whereStatement = null;

		$query = "
    		SELECT
    		    subscriberID,
    		    stripeCustomerID,
    		    stripeSubscriptionID,
    		    chargeMaximumCount,
    		    chargeCurrentCount,
    		    status,
    		    email,
    		    planID,
    		    quantity,
    		    created,
    		    cancelled,
    		    livemode,
    		    formId,
    		    formName
    		FROM
    		    {$dbPrefix}fullstripe_subscribers
        ";

		list( $searchText, $subscriptionStatus, $apiMode ) = $this->getFilterParameters();

		if ( isset( $searchText ) ) {
			$escapedSearchText = esc_sql( $searchText );

			$this->extendWhereStatement(
				$whereStatement,
				sprintf( "(LOWER(name) LIKE LOWER('%s') OR LOWER(email) LIKE LOWER('%s') OR LOWER(stripeSubscriptionID) like LOWER('%s') OR stripeCustomerID LIKE LOWER('%s'))", "%{$escapedSearchText}%", "%{$escapedSearchText}%", "%{$escapedSearchText}%", "%{$escapedSearchText}%" )
			);
		}

		if ( isset( $subscriptionStatus ) && $subscriptionStatus !== MM_WPFS_Admin_Menu::PARAM_VALUE_SUBSCRIPTION_STATUS_ALL ) {
			$condition = sprintf( "(status = '%s')", esc_sql( $subscriptionStatus ) );

			$this->extendWhereStatement( $whereStatement, $condition );
		}

		if ( isset( $apiMode ) && $apiMode !== MM_WPFS_Admin_Menu::PARAM_VALUE_API_MODE_ALL ) {
			$this->extendWhereStatement(
				$whereStatement,
				sprintf( '(livemode = %d)', $apiMode === MM_WPFS_Admin_Menu::PARAM_VALUE_API_MODE_LIVE ? 1 : 0 )
			);
		}

		if ( isset( $whereStatement ) ) {
			$query .= $whereStatement;
		}

		$this->addOrderByStatementToQuery( $query );

		return $query;
	}

	/**
	 * @return array
	 */
	public function get_columns() {
		return [
			self::COLUMN_PAYMENT_METHOD => '',
			self::COLUMN_ID_DATE => $this->createCompositeColumnLabel(
				/* translators: Name of the 'ID' column */
				__( 'ID', 'wp-full-stripe-free' ),
				/* translators: Name of the 'Date' column */
				__( 'Date', 'wp-full-stripe-free' )
			),
			self::COLUMN_SUBSCRIBER_FORM => $this->createCompositeColumnLabel(
				/* translators: Name of the 'Subscriber' column */
				__( 'Subscriber', 'wp-full-stripe-free' ),
				/* translators: Name of the 'Form' column */
				__( 'Form', 'wp-full-stripe-free' )
			),
			self::COLUMN_AMOUNT_FREQUENCY => $this->createCompositeColumnLabel(
				/* translators: Name of the 'Amount' column */
				__( 'Amount', 'wp-full-stripe-free' ),
				/* translators: Name of the 'Frequency' column */
				__( 'Frequency', 'wp-full-stripe-free' )
			),
			self::COLUMN_STATUS_MODE => $this->createCompositeColumnLabel(
				/* translators: Name of the 'Status' column */
				__( 'Status', 'wp-full-stripe-free' ),
				/* translators: Name of the 'Mode' column */
				__( 'Mode', 'wp-full-stripe-free' )
			),
			self::COLUMN_ACTIONS => '',
		];
	}

	/**
	 * @return array
	 */
	protected function get_sortable_columns() {
		return [];
	}

	/**
	 *
	 */
	public function no_items() {
		_e( 'No subscriptions found.', 'wp-full-stripe-free' );
	}

	/**
	 * @param bool $withId
	 */
	public function print_column_headers( $withId = true ) {
		list( $columns, $hidden, $sortable, $primary ) = $this->get_column_info();

		$columnHeader = '<tr class="wpfs-data-table__tr">';
		foreach ( $columns as $key => $displayName ) {
			$cssClasses = [ 'wpfs-data-table__th' ];
			switch ( $key ) {
				case self::COLUMN_PAYMENT_METHOD:
					array_push( $cssClasses, 'wpfs-data-table__th--w40' );
					break;

				case self::COLUMN_AMOUNT_FREQUENCY:
					array_push( $cssClasses, 'wpfs-data-table__th--right' );
			}

			$classAttributeValue = implode( " ", $cssClasses );
			$columnHeader .= "<th class='{$classAttributeValue}'>{$displayName}</th>";
		}
		$columnHeader .= '</tr>';

		echo $columnHeader;
	}

	/**
	 * @param $subscription
	 *
	 * @return string
	 */
	protected function renderIdDateCell( $subscription ): string {
		$dateLabel = MM_WPFS_Utils::formatTimestampWithWordpressDateTimeFormat( strtotime( $subscription->created ) );

		$html = "
         <td class='wpfs-data-table__td'>
            <a class='wpfs-btn wpfs-btn-link js-open-subscription-details'>{$subscription->stripeSubscriptionID}</a>
            <br>
            <div class='wpfs-typo-body wpfs-typo-body--sm'>{$dateLabel}</div>
        </td>";

		return $html;
	}


	/**
	 * @param $subscription
	 * @return string
	 */
	protected function renderSubscriberFormCell( $subscription ) {
		$formDisplayName = $this->lookupFormDisplayName( $subscription->formName );

		$html = "
        <td class='wpfs-data-table__td'>
            <div class='wpfs-typo-body wpfs-typo-body--gunmetal'>{$subscription->email}</div>
            <div class='wpfs-typo-body wpfs-typo-body--sm'>{$formDisplayName}</div>
        </td>";

		return $html;
	}

	/**
	 * @param $subscription
	 * @param $stripePlan
	 *
	 * @return string
	 * @throws Exception
	 */
	protected function renderAmountFrequencyCell( $subscription, $stripePlan ): string {
		$frequencyLabel = '';
		$amountLabel = MM_WPFS_Admin::getSubscriptionAmountLabel( $this->staticContext, $subscription, $stripePlan );

		if ( ! is_null( $stripePlan ) ) {
			$frequencyLabel = MM_WPFS_Admin::getSubscriptionIntervalLabel( $stripePlan->recurring->interval, $stripePlan->recurring->interval_count );
		}

		$html = "
            <td class='wpfs-data-table__td wpfs-data-table__td--right'>
              <strong>{$amountLabel}</strong>
              <div class='wpfs-typo-body wpfs-typo-body--sm'>{$frequencyLabel}</div>
            </td>";

		return $html;
	}

	protected function determineSubscriptionStatusCss( $subscriptionStatus ) {
		$cssStyle = '';

		switch ( $subscriptionStatus ) {
			case MM_WPFS::SUBSCRIBER_STATUS_RUNNING:
				$cssStyle = 'wpfs-tag--filled-green';
				break;

			case MM_WPFS::SUBSCRIBER_STATUS_CANCELLED:
			case MM_WPFS::SUBSCRIBER_STATUS_ENDED:
				$cssStyle = 'wpfs-tag--filled-blue';
				break;

			case MM_WPFS::SUBSCRIBER_STATUS_INCOMPLETE:
			default:
				$cssStyle = 'wpfs-tag--filled-grey';
				break;
		}

		return $cssStyle;
	}

	/**
	 * @param $subscription
	 * @return string
	 */
	protected function renderStatusModeCell( $subscription ): string {
		$statusCssStyle = $this->determineSubscriptionStatusCss( $subscription->status );
		$modeLabel = MM_WPFS_Admin::getApiModeLabelFromInteger( $subscription->livemode );
		$statusLabel = MM_WPFS_Admin::getSubscriberStatusLabelByForm( $subscription );

		$html = "
        <td class='wpfs-data-table__td'>
            <div class='wpfs-tags'>
                <span class='wpfs-tag {$statusCssStyle}'>{$statusLabel}</span>
                <span class='wpfs-tag wpfs-tag--outline'>{$modeLabel}</span>
            </div>
        </td>
        ";

		return $html;
	}

	/**
	 * @param $subscription
	 *
	 * @return string
	 */
	protected function renderActionsCell( $subscription ): string {
		$html = "<td class='wpfs-data-table__td wpfs-data-table__td--right wpfs-data-table__td--actions'>";

		if ( MM_WPFS::SUBSCRIBER_STATUS_RUNNING == $subscription->status ) {
			$cancelLabel =
				/* translators: 'Cancel' action label for subscriptions */
				__( 'Cancel', 'wp-full-stripe-free' );

			$html .= "<a class='wpfs-btn wpfs-btn-icon wpfs-btn-icon--20 js-tooltip js-cancel-subscription' data-tooltip-content='cancel-subscription-tooltip'>
                <span class='wpfs-icon-cancel'></span>
            </a>
            <div class='wpfs-tooltip-content' data-tooltip-id='cancel-subscription-tooltip'>
                <div class='wpfs-info-tooltip'>{$cancelLabel}</div>
            </div>";
		}

		$deleteLabel =
			/* translators: 'Delete' action label for subscriptions */
			__( 'Delete subscription', 'wp-full-stripe-free' );
		$html .= "<a class='wpfs-btn wpfs-btn-icon wpfs-btn-icon--20 js-tooltip js-delete-subscription' data-tooltip-content='delete-subscription-tooltip'>
                <span class='wpfs-icon-trash'></span>
            </a>
            <div class='wpfs-tooltip-content' data-tooltip-id='delete-subscription-tooltip'>
                <div class='wpfs-info-tooltip'>{$deleteLabel}</div>
            </div>";

		$html .= '</td>';

		return $html;
	}

	/**
	 * @param $planId string
	 * @param $liveMode bool
	 *
	 * @return \StripeWPFS\Stripe\Price|null
	 * @throws Exception
	 */
	protected function retrievePlanByMode( $planId, $liveMode ) {
		$plan = null;

		if ( $liveMode ) {
			if ( is_null( $this->stripeLive ) ) {
				$this->stripeLive = new MM_WPFS_Stripe( MM_WPFS_Stripe::getStripeAuthenticationTokenByMode( $this->staticContext, $liveMode ), $this->loggerService );
			}
			$plan = $this->stripeLive->retrievePlan( $planId );
		} else {
			if ( is_null( $this->stripeDev ) ) {
				$this->stripeDev = new MM_WPFS_Stripe( MM_WPFS_Stripe::getStripeAuthenticationTokenByMode( $this->staticContext, $liveMode ), $this->loggerService );
			}
			$plan = $this->stripeDev->retrievePlan( $planId );
		}

		return $plan;
	}

	/**
	 * @throws Exception
	 */
	public function display_rows() {
		$subscriptions = $this->items;
		list( $columns, $hidden ) = $this->get_column_info();

		if ( ! empty( $subscriptions ) ) {
			foreach ( $subscriptions as $subscription ) {
				$stripePlan = $this->retrievePlanByMode( $subscription->planID, $subscription->livemode );

				$rowHtml = "<tr class='wpfs-data-table__tr' data-db-id='{$subscription->subscriberID}' data-stripe-id='{$subscription->stripeSubscriptionID}'>";

				foreach ( $columns as $key => $displayName ) {
					switch ( $key ) {
						case self::COLUMN_PAYMENT_METHOD:
							$rowHtml .= $this->renderPaymentMethodCell( $subscription );
							break;

						case self::COLUMN_ID_DATE:
							$rowHtml .= $this->renderIdDateCell( $subscription );
							break;

						case self::COLUMN_SUBSCRIBER_FORM:
							$rowHtml .= $this->renderSubscriberFormCell( $subscription );
							break;

						case self::COLUMN_AMOUNT_FREQUENCY:
							$rowHtml .= $this->renderAmountFrequencyCell( $subscription, $stripePlan );
							break;

						case self::COLUMN_STATUS_MODE:
							$rowHtml .= $this->renderStatusModeCell( $subscription );
							break;

						case self::COLUMN_ACTIONS:
							$rowHtml .= $this->renderActionsCell( $subscription );
							break;
					}
				}

				$rowHtml .= "</tr>";
				echo $rowHtml;
			}
		}
	}
}

class WPFS_Donations_Table extends WPFS_Transactions_Table {
	const COLUMN_PAYMENT_METHOD = 'paymentMethod';
	const COLUMN_ID_DATE = 'idDate';
	const COLUMN_DONOR_FORM = 'donorForm';
	const COLUMN_AMOUNT_FREQUENCY = 'amountFrequency';
	const COLUMN_STATUS_MODE = 'statusMode';
	const COLUMN_ACTIONS = 'actions';

	public function __construct( $loggerService ) {
		parent::__construct( $loggerService, [
			'singular' =>
				/* translators: Singular version of the word 'Donation', used on the donation list page */
				__( 'Donation', 'wp-full-stripe-free' ),
			'plural' =>
				/* translators: Plural version of the word 'Donation', used on the donation list page */
				__( 'Donations', 'wp-full-stripe-free' ),
			'ajax' => false
		] );
	}

	/**
	 * @return array
	 */
	protected function getForms(): array {
		return $this->db->getDonationFormNames();
	}

	/**
	 * @return array
	 */
	protected function getFilterParameters(): array {
		$searchText = ! empty( $_REQUEST[ MM_WPFS_Admin_Menu::PARAM_NAME_DONATIONS_TEXT_FILTER ] ) ? trim( $_REQUEST[ MM_WPFS_Admin_Menu::PARAM_NAME_DONATIONS_TEXT_FILTER ] ) : null;
		$mode = ! empty( $_REQUEST[ MM_WPFS_Admin_Menu::PARAM_NAME_DONATIONS_MODE_FILTER ] ) ? trim( $_REQUEST[ MM_WPFS_Admin_Menu::PARAM_NAME_DONATIONS_MODE_FILTER ] ) : null;

		return [ $searchText, $mode ];
	}

	protected function getQuery() {
		$dbPrefix = $this->db->getDatabasePrefix();
		$whereStatement = null;

		$query = "
            SELECT
                donationID,
                stripeCustomerID,
                stripeSubscriptionID,
                stripePaymentIntentID,
                paid,
                captured,
                refunded,
                expired,
                lastChargeStatus,
                subscriptionStatus,
                currency,
                amount,
                donationFrequency,
                email,
                created,
                cancelled,
                livemode,
                formId,
                formName
            FROM
               {$dbPrefix}fullstripe_donations
        ";

		list( $searchText, $apiMode ) = $this->getFilterParameters();

		if ( isset( $searchText ) ) {
			$escapedSearchText = esc_sql( $searchText );

			$this->extendWhereStatement(
				$whereStatement,
				sprintf( "(LOWER(name) LIKE LOWER('%s') OR LOWER(email) LIKE LOWER('%s') OR LOWER(stripeSubscriptionID) like LOWER('%s') OR LOWER(stripePaymentIntentID) like LOWER('%s') OR stripeCustomerID LIKE LOWER('%s'))", "%{$escapedSearchText}%", "%{$escapedSearchText}%", "%{$escapedSearchText}%", "%{$escapedSearchText}%", "%{$escapedSearchText}%" )
			);
		}

		if ( isset( $apiMode ) && $apiMode !== MM_WPFS_Admin_Menu::PARAM_VALUE_API_MODE_ALL ) {
			$this->extendWhereStatement(
				$whereStatement,
				sprintf( '(livemode = %d)', $apiMode === MM_WPFS_Admin_Menu::PARAM_VALUE_API_MODE_LIVE ? 1 : 0 )
			);
		}

		if ( isset( $whereStatement ) ) {
			$query .= $whereStatement;
		}

		$this->addOrderByStatementToQuery( $query );

		return $query;
	}

	/**
	 * @return array
	 */
	public function get_columns() {
		return [
			self::COLUMN_PAYMENT_METHOD => '',
			self::COLUMN_ID_DATE => $this->createCompositeColumnLabel(
				/* translators: Name of the 'ID' column */
				__( 'ID', 'wp-full-stripe-free' ),
				/* translators: Name of the 'Date' column */
				__( 'Date', 'wp-full-stripe-free' )
			),
			self::COLUMN_DONOR_FORM => $this->createCompositeColumnLabel(
				/* translators: Name of the 'Donor' column */
				__( 'Donor', 'wp-full-stripe-free' ),
				/* translators: Name of the 'Form' column */
				__( 'Form', 'wp-full-stripe-free' )
			),
			self::COLUMN_AMOUNT_FREQUENCY => $this->createCompositeColumnLabel(
				/* translators: Name of the 'Amount' column */
				__( 'Amount', 'wp-full-stripe-free' ),
				/* translators: Name of the 'Frequency' column */
				__( 'Frequency', 'wp-full-stripe-free' )
			),
			self::COLUMN_STATUS_MODE => $this->createCompositeColumnLabel(
				/* translators: Name of the 'Status' column */
				__( 'Status', 'wp-full-stripe-free' ),
				/* translators: Name of the 'Mode' column */
				__( 'Mode', 'wp-full-stripe-free' )
			),
			self::COLUMN_ACTIONS => '',
		];
	}

	/**
	 * @return array
	 */
	protected function get_sortable_columns() {
		return [];
	}

	/**
	 *
	 */
	public function no_items() {
		_e( 'No donation found.', 'wp-full-stripe-free' );
	}

	/**
	 * @param bool $withId
	 */
	public function print_column_headers( $withId = true ) {
		list( $columns, $hidden, $sortable, $primary ) = $this->get_column_info();

		$columnHeader = '<tr class="wpfs-data-table__tr">';
		foreach ( $columns as $key => $displayName ) {
			$cssClasses = [ 'wpfs-data-table__th' ];
			switch ( $key ) {
				case self::COLUMN_PAYMENT_METHOD:
					array_push( $cssClasses, 'wpfs-data-table__th--w40' );
					break;

				case self::COLUMN_AMOUNT_FREQUENCY:
					array_push( $cssClasses, 'wpfs-data-table__th--right' );
			}

			$classAttributeValue = implode( " ", $cssClasses );
			$columnHeader .= "<th class='{$classAttributeValue}'>{$displayName}</th>";
		}
		$columnHeader .= '</tr>';

		echo $columnHeader;
	}

	/**
	 * @param $donation
	 *
	 * @return string
	 */
	protected function renderIdDateCell( $donation ): string {
		$dateLabel = MM_WPFS_Utils::formatTimestampWithWordpressDateTimeFormat( strtotime( $donation->created ) );

		$html = "
         <td class='wpfs-data-table__td'>
            <a class='wpfs-btn wpfs-btn-link js-open-donation-details'>{$donation->stripePaymentIntentID}</a>
            <br>
            <div class='wpfs-typo-body wpfs-typo-body--sm'>{$dateLabel}</div>
        </td>";

		return $html;
	}


	/**
	 * @param $donation
	 *
	 * @return string
	 */
	protected function renderDonorFormCell( $donation ) {
		$formDisplayName = $this->lookupFormDisplayName( $donation->formName );

		$html = "
        <td class='wpfs-data-table__td'>
            <div class='wpfs-typo-body wpfs-typo-body--gunmetal'>{$donation->email}</div>
            <div class='wpfs-typo-body wpfs-typo-body--sm'>{$formDisplayName}</div>
        </td>";

		return $html;
	}

	/**
	 * @param $donationFrequency
	 *
	 * @return string
	 */
	protected function generateDonationFrequencyLabel( $donationFrequency ) {
		$label = "Unknown";
		switch ( $donationFrequency ) {
			case MM_WPFS_DonationFormViewConstants::FIELD_VALUE_DONATION_FREQUENCY_ONE_TIME:
				$label =
					/* translators: Donation frequency 'one-time' displayed in the donation list */
					__( "one-time", 'wp-full-stripe-free' );
				break;

			case MM_WPFS_DonationFormViewConstants::FIELD_VALUE_DONATION_FREQUENCY_DAILY:
				$label =
					/* translators: Donation frequency 'daily' displayed in the donation list */
					__( "daily", 'wp-full-stripe-free' );
				break;

			case MM_WPFS_DonationFormViewConstants::FIELD_VALUE_DONATION_FREQUENCY_WEEKLY:
				$label =
					/* translators: Donation frequency 'weekly' displayed in the donation list */
					__( "weekly", 'wp-full-stripe-free' );
				break;

			case MM_WPFS_DonationFormViewConstants::FIELD_VALUE_DONATION_FREQUENCY_MONTHLY:
				$label =
					/* translators: Donation frequency 'monthly' displayed in the donation list */
					__( "monthly", 'wp-full-stripe-free' );
				break;

			case MM_WPFS_DonationFormViewConstants::FIELD_VALUE_DONATION_FREQUENCY_ANNUAL:
				$label =
					/* translators: Donation frequency 'annual' displayed in the donation list */
					__( "annual", 'wp-full-stripe-free' );
				break;
		}

		return $label;
	}

	/**
	 * @param $donation
	 *
	 * @return string
	 * @throws Exception
	 */
	protected function renderAmountFrequencyCell( $donation ): string {
		$amountLabel = MM_WPFS_Currencies::formatAndEscape( $this->staticContext, $donation->currency, $donation->amount );
		$frequencyLabel = $this->generateDonationFrequencyLabel( $donation->donationFrequency );

		$html = "
            <td class='wpfs-data-table__td wpfs-data-table__td--right'>
              <strong>{$amountLabel}</strong>
              <div class='wpfs-typo-body wpfs-typo-body--sm'>{$frequencyLabel}</div>
            </td>";

		return $html;
	}


	/**
	 * @param $donationStatus
	 *
	 * @return string
	 */
	protected function determineDonationStatusCss( $donationStatus ): string {
		$cssStyle = '';

		switch ( $donationStatus ) {
			case MM_WPFS::DONATION_STATUS_PAID:
			case MM_WPFS::DONATION_STATUS_RUNNING:
				$cssStyle = 'wpfs-tag--filled-green';
				break;

			case MM_WPFS::DONATION_STATUS_REFUNDED:
			case MM_WPFS::DONATION_STATUS_UNKNOWN:
			default:
				$cssStyle = 'wpfs-tag--filled-grey';
				break;
		}

		return $cssStyle;
	}

	/**
	 * @param $donation
	 * @return string
	 */
	protected function renderStatusModeCell( $donation ): string {
		$status = MM_WPFS_Utils::getDonationStatus( $donation );

		$statusLabel = MM_WPFS_Admin::getDonationStatusLabel( $status );
		$statusCssStyle = $this->determineDonationStatusCss( $status );
		$modeLabel = MM_WPFS_Admin::getApiModeLabelFromInteger( $donation->livemode );

		$html = "
        <td class='wpfs-data-table__td'>
            <div class='wpfs-tags'>
                <span class='wpfs-tag {$statusCssStyle}'>{$statusLabel}</span>
                <span class='wpfs-tag wpfs-tag--outline'>{$modeLabel}</span>
            </div>
        </td>
        ";

		return $html;
	}

	/**
	 * @param $donation
	 *
	 * @return string
	 */
	protected function renderActionsCell( $donation ): string {
		$status = MM_WPFS_Utils::getDonationPaymentStatus( $donation );

		$html = "<td class='wpfs-data-table__td wpfs-data-table__td--right wpfs-data-table__td--actions'>";

		if ( MM_WPFS::PAYMENT_STATUS_PAID === $status ) {
			$refundLabel =
				/* translators: 'Refund' action label for donations */
				__( 'Refund', 'wp-full-stripe-free' );

			$html .= "<a class='wpfs-btn wpfs-btn-icon wpfs-btn-icon--20 js-tooltip js-refund-donation' data-tooltip-content='refund-donation-tooltip'>
                <span class='wpfs-icon-sync'></span>
            </a>
            <div class='wpfs-tooltip-content' data-tooltip-id='refund-donation-tooltip'>
                <div class='wpfs-info-tooltip'>{$refundLabel}</div>
            </div>";

		}

		if ( \StripeWPFS\Stripe\Subscription::STATUS_ACTIVE === $donation->subscriptionStatus ) {
			$cancelLabel =
				/* translators: 'Cancel' action label for donations */
				__( 'Cancel', 'wp-full-stripe-free' );

			$html .= "<a class='wpfs-btn wpfs-btn-icon wpfs-btn-icon--20 js-tooltip js-cancel-donation' data-tooltip-content='cancel-donation-tooltip'>
                <span class='wpfs-icon-cancel'></span>
            </a>
            <div class='wpfs-tooltip-content' data-tooltip-id='cancel-donation-tooltip'>
                <div class='wpfs-info-tooltip'>{$cancelLabel}</div>
            </div>";
		}

		$deleteLabel =
			/* translators: 'Delete' action label for donations */
			__( 'Delete donation', 'wp-full-stripe-free' );
		$html .= "<a class='wpfs-btn wpfs-btn-icon wpfs-btn-icon--20 js-tooltip js-delete-donation' data-tooltip-content='delete-donation-tooltip'>
                <span class='wpfs-icon-trash'></span>
            </a>
            <div class='wpfs-tooltip-content' data-tooltip-id='delete-donation-tooltip'>
                <div class='wpfs-info-tooltip'>{$deleteLabel}</div>
            </div>";

		$html .= '</td>';

		return $html;
	}

	/**
	 * @throws Exception
	 */
	public function display_rows() {
		$donations = $this->items;
		list( $columns, $hidden ) = $this->get_column_info();

		if ( ! empty( $donations ) ) {
			foreach ( $donations as $donation ) {
				$rowHtml = "<tr class='wpfs-data-table__tr' data-db-id='{$donation->donationID}' data-stripe-id='{$donation->stripePaymentIntentID}'>";

				foreach ( $columns as $key => $displayName ) {
					switch ( $key ) {
						case self::COLUMN_PAYMENT_METHOD:
							$rowHtml .= $this->renderPaymentMethodCell( $donation );
							break;

						case self::COLUMN_ID_DATE:
							$rowHtml .= $this->renderIdDateCell( $donation );
							break;

						case self::COLUMN_DONOR_FORM:
							$rowHtml .= $this->renderDonorFormCell( $donation );
							break;

						case self::COLUMN_AMOUNT_FREQUENCY:
							$rowHtml .= $this->renderAmountFrequencyCell( $donation );
							break;

						case self::COLUMN_STATUS_MODE:
							$rowHtml .= $this->renderStatusModeCell( $donation );
							break;

						case self::COLUMN_ACTIONS:
							$rowHtml .= $this->renderActionsCell( $donation );
							break;
					}
				}

				$rowHtml .= "</tr>";
				echo $rowHtml;
			}
		}
	}
}

class WPFS_SavedCards_Table extends WPFS_Transactions_Table {
	const COLUMN_PAYMENT_METHOD = 'paymentMethod';
	const COLUMN_ID_DATE = 'idDate';
	const COLUMN_CUSTOMER_FORM = 'customerForm';
	const COLUMN_MODE = 'mode';
	const COLUMN_ACTIONS = 'actions';

	public function __construct( $loggerService ) {
		parent::__construct( $loggerService, [
			'singular' =>
				/* translators: Singular version of the expression 'Saved card', used on the save card list page */
				__( 'Saved card', 'wp-full-stripe-free' ),
			'plural' =>
				/* translators: Plural version of the expression 'Saved card', used on the save card list page */
				__( 'Saved cards', 'wp-full-stripe-free' ),
			'ajax' => false
		] );
	}

	/**
	 * @return array
	 */
	protected function getForms(): array {
		return $this->db->getSaveCardFormNames();
	}

	/**
	 * @return array
	 */
	protected function getFilterParameters(): array {
		$searchText = ! empty( $_REQUEST[ MM_WPFS_Admin_Menu::PARAM_NAME_SAVED_CARDS_TEXT_FILTER ] ) ? trim( $_REQUEST[ MM_WPFS_Admin_Menu::PARAM_NAME_SAVED_CARDS_TEXT_FILTER ] ) : null;
		$mode = ! empty( $_REQUEST[ MM_WPFS_Admin_Menu::PARAM_NAME_SAVED_CARDS_MODE_FILTER ] ) ? trim( $_REQUEST[ MM_WPFS_Admin_Menu::PARAM_NAME_SAVED_CARDS_MODE_FILTER ] ) : null;

		return [ $searchText, $mode ];
	}

	/**
	 * @return string
	 */
	protected function getQuery() {
		$dbPrefix = $this->db->getDatabasePrefix();
		$whereStatement = null;

		$query = "
            SELECT
                captureID,
                stripeCustomerID,
                email,
                created,
                livemode,
                formId,
                formName
            FROM
               {$dbPrefix}fullstripe_card_captures
        ";

		list( $searchText, $apiMode ) = $this->getFilterParameters();

		if ( isset( $searchText ) ) {
			$escapedSearchText = esc_sql( $searchText );

			$this->extendWhereStatement(
				$whereStatement,
				sprintf( "(LOWER(name) LIKE LOWER('%s') OR LOWER(email) LIKE LOWER('%s') OR stripeCustomerID LIKE LOWER('%s'))", "%{$escapedSearchText}%", "%{$escapedSearchText}%", "%{$escapedSearchText}%" )
			);
		}

		if ( isset( $apiMode ) && $apiMode !== MM_WPFS_Admin_Menu::PARAM_VALUE_API_MODE_ALL ) {
			$this->extendWhereStatement(
				$whereStatement,
				sprintf( '(livemode = %d)', $apiMode === MM_WPFS_Admin_Menu::PARAM_VALUE_API_MODE_LIVE ? 1 : 0 )
			);
		}

		if ( isset( $whereStatement ) ) {
			$query .= $whereStatement;
		}

		$this->addOrderByStatementToQuery( $query );

		return $query;
	}

	/**
	 * @return array
	 */
	public function get_columns() {
		return [
			self::COLUMN_PAYMENT_METHOD => '',
			self::COLUMN_ID_DATE => $this->createCompositeColumnLabel(
				/* translators: Name of the 'ID' column */
				__( 'ID', 'wp-full-stripe-free' ),
				/* translators: Name of the 'Date' column */
				__( 'Date', 'wp-full-stripe-free' )
			),
			self::COLUMN_CUSTOMER_FORM => $this->createCompositeColumnLabel(
				/* translators: Name of the 'Customer' column */
				__( 'Customer', 'wp-full-stripe-free' ),
				/* translators: Name of the 'Form' column */
				__( 'Form', 'wp-full-stripe-free' )
			),
			self::COLUMN_MODE =>
				/* translators: Name of the 'Mode' column */
				__( 'Mode', 'wp-full-stripe-free' ),
			self::COLUMN_ACTIONS => ''
		];
	}

	/**
	 * @return array
	 */
	protected function get_sortable_columns() {
		return [];
	}

	/**
	 *
	 */
	public function no_items() {
		_e( 'No saved card found.', 'wp-full-stripe-free' );
	}

	/**
	 * @param bool $withId
	 */
	public function print_column_headers( $withId = true ) {
		list( $columns, $hidden, $sortable, $primary ) = $this->get_column_info();

		$columnHeader = '<tr class="wpfs-data-table__tr">';
		foreach ( $columns as $key => $displayName ) {
			$cssClasses = [ 'wpfs-data-table__th' ];
			switch ( $key ) {
				case self::COLUMN_PAYMENT_METHOD:
					array_push( $cssClasses, 'wpfs-data-table__th--w40' );
					break;
			}

			$classAttributeValue = implode( " ", $cssClasses );
			$columnHeader .= "<th class='{$classAttributeValue}'>{$displayName}</th>";
		}
		$columnHeader .= '</tr>';

		echo $columnHeader;
	}

	/**
	 * @param $savedCard
	 *
	 * @return string
	 */
	protected function renderIdDateCell( $savedCard ): string {
		$dateLabel = MM_WPFS_Utils::formatTimestampWithWordpressDateTimeFormat( strtotime( $savedCard->created ) );

		$html = "
         <td class='wpfs-data-table__td'>
            <a class='wpfs-btn wpfs-btn-link js-open-saved-card-details'>{$savedCard->stripeCustomerID}</a>
            <br>
            <div class='wpfs-typo-body wpfs-typo-body--sm'>{$dateLabel}</div>
        </td>";

		return $html;
	}

	/**
	 * @param $savedCard
	 * @return string
	 */
	protected function renderCustomerFormCell( $savedCard ) {
		$formDisplayName = $this->lookupFormDisplayName( $savedCard->formName );

		$html = "
        <td class='wpfs-data-table__td'>
            <div class='wpfs-typo-body wpfs-typo-body--gunmetal'>{$savedCard->email}</div>
            <div class='wpfs-typo-body wpfs-typo-body--sm'>{$formDisplayName}</div>
        </td>";

		return $html;
	}

	/**
	 * @param $savedCard
	 * @return string
	 */
	protected function renderModeCell( $savedCard ): string {
		$modeLabel = MM_WPFS_Admin::getApiModeLabelFromInteger( $savedCard->livemode );

		$html = "
        <td class='wpfs-data-table__td'>
            <div class='wpfs-tags'>
                <span class='wpfs-tag wpfs-tag--outline'>{$modeLabel}</span>
            </div>
        </td>
        ";

		return $html;
	}

	protected function renderActionsCell( $payment ) {
		$html = "<td class='wpfs-data-table__td wpfs-data-table__td--right wpfs-data-table__td--actions'>";

		$deleteLabel =
			/* translators: 'Delete' action label for saved cards */
			__( 'Delete saved card', 'wp-full-stripe-free' );
		$html .= "<a class='wpfs-btn wpfs-btn-icon wpfs-btn-icon--20 js-tooltip js-delete-saved-card' data-tooltip-content='delete-saved-card-tooltip'>
                <span class='wpfs-icon-trash'></span>
            </a>
            <div class='wpfs-tooltip-content' data-tooltip-id='delete-saved-card-tooltip'>
                <div class='wpfs-info-tooltip'>{$deleteLabel}</div>
            </div>";

		$html .= '</td>';

		return $html;
	}

	public function display_rows() {
		$savedCards = $this->items;
		list( $columns, $hidden ) = $this->get_column_info();

		if ( ! empty( $savedCards ) ) {
			foreach ( $savedCards as $savedCard ) {
				$rowHtml = "<tr class='wpfs-data-table__tr' data-db-id='{$savedCard->captureID}' data-stripe-id='{$savedCard->stripeCustomerID}'>";

				foreach ( $columns as $key => $displayName ) {
					switch ( $key ) {
						case self::COLUMN_PAYMENT_METHOD:
							$rowHtml .= $this->renderPaymentMethodCell( $savedCard );
							break;

						case self::COLUMN_ID_DATE:
							$rowHtml .= $this->renderIdDateCell( $savedCard );
							break;

						case self::COLUMN_CUSTOMER_FORM:
							$rowHtml .= $this->renderCustomerFormCell( $savedCard );
							break;

						case self::COLUMN_MODE:
							$rowHtml .= $this->renderModeCell( $savedCard );
							break;

						case self::COLUMN_ACTIONS:
							$rowHtml .= $this->renderActionsCell( $savedCard );
							break;
					}
				}

				$rowHtml .= "</tr>";
				echo $rowHtml;
			}
		}
	}

}

class WPFS_OneTimePayments_Table extends WPFS_Transactions_Table {
	const COLUMN_PAYMENT_METHOD = 'paymentMethod';
	const COLUMN_ID_DATE = 'idDate';
	const COLUMN_CUSTOMER_FORM = 'customerForm';
	const COLUMN_AMOUNT = 'amount';
	const COLUMN_STATUS_MODE = 'statusMode';
	const COLUMN_ACTIONS = 'actions';

	public function __construct( $loggerService ) {
		parent::__construct( $loggerService, [
			'singular' =>
				/* translators: Singular version of the word 'Payment', used on the payment list page */
				__( 'Payment', 'wp-full-stripe-free' ),
			'plural' =>
				/* translators: Plural version of the word 'Payment', used on the payment list page */
				__( 'Payments', 'wp-full-stripe-free' ),
			'ajax' => false
		] );
	}

	/**
	 * @return array
	 */
	protected function getForms(): array {
		return $this->db->getOneTimePaymentFormNames();
	}

	/**
	 * @return array
	 */
	protected function getFilterParameters(): array {
		$searchText = ! empty( $_REQUEST[ MM_WPFS_Admin_Menu::PARAM_NAME_PAYMENTS_TEXT_FILTER ] ) ? trim( $_REQUEST[ MM_WPFS_Admin_Menu::PARAM_NAME_PAYMENTS_TEXT_FILTER ] ) : null;
		$status = ! empty( $_REQUEST[ MM_WPFS_Admin_Menu::PARAM_NAME_PAYMENTS_STATUS_FILTER ] ) ? trim( $_REQUEST[ MM_WPFS_Admin_Menu::PARAM_NAME_PAYMENTS_STATUS_FILTER ] ) : null;
		$mode = ! empty( $_REQUEST[ MM_WPFS_Admin_Menu::PARAM_NAME_PAYMENTS_MODE_FILTER ] ) ? trim( $_REQUEST[ MM_WPFS_Admin_Menu::PARAM_NAME_PAYMENTS_MODE_FILTER ] ) : null;

		return [ $searchText, $status, $mode ];
	}

	protected function getQuery() {
		$dbPrefix = $this->db->getDatabasePrefix();
		$whereStatement = null;

		$query = "
            SELECT
                paymentID,
                eventID,
                stripeCustomerID,
                payment_method,
                paid,
                captured,
                refunded,
                expired,
                failure_code,
                failure_message,
                livemode,
                last_charge_status,
                currency,
                amount,
                name,
                email,
                formId,
                formType,
                formName,
                created
            FROM
                {$dbPrefix}fullstripe_payments
        ";

		list( $searchText, $paymentStatus, $apiMode ) = $this->getFilterParameters();

		if ( isset( $searchText ) ) {
			$escapedSearchText = esc_sql( $searchText );

			$this->extendWhereStatement(
				$whereStatement,
				sprintf( "(LOWER(name) LIKE LOWER('%s') OR LOWER(email) LIKE LOWER('%s') OR LOWER(eventID) like LOWER('%s') OR stripeCustomerID LIKE LOWER('%s'))", "%{$escapedSearchText}%", "%{$escapedSearchText}%", "%{$escapedSearchText}%", "%{$escapedSearchText}%" )
			);
		}

		if ( isset( $paymentStatus ) && $paymentStatus !== MM_WPFS_Admin_Menu::PARAM_VALUE_PAYMENT_STATUS_ALL ) {
			$condition = '';

			if ( MM_WPFS::PAYMENT_STATUS_FAILED === $paymentStatus ) {
				$condition .= sprintf( "last_charge_status='%s'", MM_WPFS::STRIPE_CHARGE_STATUS_FAILED );
			} elseif ( MM_WPFS::PAYMENT_STATUS_PENDING === $paymentStatus ) {
				$condition .= sprintf( "last_charge_status='%s'", MM_WPFS::STRIPE_CHARGE_STATUS_PENDING );
			} elseif ( MM_WPFS::PAYMENT_STATUS_EXPIRED === $paymentStatus ) {
				$condition .= sprintf( "expired=%d", 1 );
			} elseif ( MM_WPFS::PAYMENT_STATUS_REFUNDED === $paymentStatus ) {
				$condition .= sprintf( "refunded=%d", 1 );
			} elseif ( MM_WPFS::PAYMENT_STATUS_RELEASED === $paymentStatus ) {
				$condition .= sprintf( "(refunded=%d AND captured=%d)", 1, 0 );
			} elseif ( MM_WPFS::PAYMENT_STATUS_PAID === $paymentStatus ) {
				$condition .= sprintf( "(last_charge_status='%s' AND paid=%d AND captured=%d AND expired=%d AND refunded=%d)", MM_WPFS::STRIPE_CHARGE_STATUS_SUCCEEDED, 1, 1, 0, 0 );
			} elseif ( MM_WPFS::PAYMENT_STATUS_AUTHORIZED === $paymentStatus ) {
				$condition .= sprintf( "(last_charge_status='%s' AND paid=%d AND captured=%d AND expired=%d AND refunded=%d)", MM_WPFS::STRIPE_CHARGE_STATUS_SUCCEEDED, 1, 0, 0, 0 );
			}

			$this->extendWhereStatement( $whereStatement, $condition );
		}

		if ( isset( $apiMode ) && $apiMode !== MM_WPFS_Admin_Menu::PARAM_VALUE_API_MODE_ALL ) {
			$this->extendWhereStatement(
				$whereStatement,
				sprintf( '(livemode = %d)', $apiMode === MM_WPFS_Admin_Menu::PARAM_VALUE_API_MODE_LIVE ? 1 : 0 )
			);
		}

		if ( isset( $whereStatement ) ) {
			$query .= $whereStatement;
		}

		$this->addOrderByStatementToQuery( $query );

		return $query;
	}

	/**
	 * @return array
	 */
	public function get_columns() {
		return [
			self::COLUMN_PAYMENT_METHOD => '',
			self::COLUMN_ID_DATE => $this->createCompositeColumnLabel(
				/* translators: Name of the 'ID' column */
				__( 'ID', 'wp-full-stripe-free' ),
				/* translators: Name of the 'Date' column */
				__( 'Date', 'wp-full-stripe-free' )
			),
			self::COLUMN_CUSTOMER_FORM => $this->createCompositeColumnLabel(
				/* translators: Name of the 'Customer' column */
				__( 'Customer', 'wp-full-stripe-free' ),
				/* translators: Name of the 'Form' column */
				__( 'Form', 'wp-full-stripe-free' )
			),
			self::COLUMN_AMOUNT =>
				/* translators: Name of the 'Amount' column */
				__( 'Amount', 'wp-full-stripe-free' ),
			self::COLUMN_STATUS_MODE => $this->createCompositeColumnLabel(
				/* translators: Name of the 'Status' column */
				__( 'Status', 'wp-full-stripe-free' ),
				/* translators: Name of the 'Mode' column */
				__( 'Mode', 'wp-full-stripe-free' )
			),
			self::COLUMN_ACTIONS => '',
		];
	}

	/**
	 * @return array
	 */
	protected function get_sortable_columns() {
		return [];
	}

	/**
	 *
	 */
	public function no_items() {
		_e( 'No payment found.', 'wp-full-stripe-free' );
	}

	/**
	 * @param bool $withId
	 */
	public function print_column_headers( $withId = true ) {
		list( $columns, $hidden, $sortable, $primary ) = $this->get_column_info();

		$columnHeader = '<tr class="wpfs-data-table__tr">';
		foreach ( $columns as $key => $displayName ) {
			$cssClasses = [ 'wpfs-data-table__th' ];
			switch ( $key ) {
				case self::COLUMN_PAYMENT_METHOD:
					array_push( $cssClasses, 'wpfs-data-table__th--w40' );
					break;

				case self::COLUMN_AMOUNT:
					array_push( $cssClasses, 'wpfs-data-table__th--right' );
			}

			$classAttributeValue = implode( " ", $cssClasses );
			$columnHeader .= "<th class='{$classAttributeValue}'>{$displayName}</th>";
		}
		$columnHeader .= '</tr>';

		echo $columnHeader;
	}

	/**
	 * @param $payment
	 *
	 * @return string
	 */
	protected function renderIdDateCell( $payment ): string {
		$dateLabel = MM_WPFS_Utils::formatTimestampWithWordpressDateTimeFormat( strtotime( $payment->created ) );

		$html = "
         <td class='wpfs-data-table__td'>
            <a class='wpfs-btn wpfs-btn-link js-open-payment-details'>{$payment->eventID}</a>
            <br>
            <div class='wpfs-typo-body wpfs-typo-body--sm'>{$dateLabel}</div>
        </td>";

		return $html;
	}

	/**
	 * @param $payment
	 * @return string
	 */
	protected function renderCustomerFormCell( $payment ) {
		$formDisplayName = $this->lookupFormDisplayName( $payment->formName );

		$html = "
        <td class='wpfs-data-table__td'>
            <div class='wpfs-typo-body wpfs-typo-body--gunmetal'>{$payment->email}</div>
            <div class='wpfs-typo-body wpfs-typo-body--sm'>{$formDisplayName}</div>
        </td>";

		return $html;
	}

	protected function renderAmountCell( $payment ) {
		$amountLabel = MM_WPFS_Currencies::formatAndEscape( $this->staticContext, $payment->currency, $payment->amount );

		$html = "
        <td class='wpfs-data-table__td wpfs-data-table__td--right'>
            <strong>{$amountLabel}</strong>
        </td>";

		return $html;
	}

	protected function determinePaymentStatusCss( $paymentStatus ) {
		$cssStyle = '';

		switch ( $paymentStatus ) {
			case MM_WPFS::PAYMENT_STATUS_PAID:
				$cssStyle = 'wpfs-tag--filled-green';
				break;

			case MM_WPFS::PAYMENT_STATUS_AUTHORIZED:
				$cssStyle = 'wpfs-tag--filled-blue';
				break;

			case MM_WPFS::PAYMENT_STATUS_FAILED:
			case MM_WPFS::PAYMENT_STATUS_UNKNOWN:
				$cssStyle = 'wpfs-tag--filled-red';
				break;

			case MM_WPFS::PAYMENT_STATUS_PENDING:
			case MM_WPFS::PAYMENT_STATUS_EXPIRED:
			case MM_WPFS::PAYMENT_STATUS_REFUNDED:
			case MM_WPFS::PAYMENT_STATUS_RELEASED:
			default:
				$cssStyle = 'wpfs-tag--filled-grey';
				break;
		}

		return $cssStyle;
	}

	/**
	 * @param $payment
	 * @return string
	 */
	protected function renderStatusModeCell( $payment ) {
		$paymentStatus = MM_WPFS_Utils::getPaymentStatus( $payment );

		$statusCssStyle = $this->determinePaymentStatusCss( $paymentStatus );
		$statusLabel = MM_WPFS_Admin::getPaymentStatusLabel( $paymentStatus );
		$modeLabel = MM_WPFS_Admin::getApiModeLabelFromInteger( $payment->livemode );

		$html = "
        <td class='wpfs-data-table__td'>
            <div class='wpfs-tags'>
                <span class='wpfs-tag {$statusCssStyle}'>{$statusLabel}</span>
                <span class='wpfs-tag wpfs-tag--outline'>{$modeLabel}</span>
            </div>
        </td>
        ";

		return $html;
	}

	protected function renderActionsCell( $payment ) {
		$paymentStatus = MM_WPFS_Utils::getPaymentStatus( $payment );

		$html = "<td class='wpfs-data-table__td wpfs-data-table__td--right wpfs-data-table__td--actions'>";

		if ( MM_WPFS::PAYMENT_STATUS_AUTHORIZED == $paymentStatus ) {
			$captureLabel =
				/* translators: 'Capture' action label for payments */
				__( 'Capture', 'wp-full-stripe-free' );

			$html .= "<a class='wpfs-btn wpfs-btn-icon wpfs-btn-icon--20 js-tooltip js-capture-payment' data-tooltip-content='authorize-tooltip'>
                <span class='wpfs-icon-card'></span>
            </a>
            <div class='wpfs-tooltip-content' data-tooltip-id='authorize-tooltip'>
                <div class='wpfs-info-tooltip'>{$captureLabel}</div>
            </div>";
		}

		if ( MM_WPFS::PAYMENT_STATUS_PAID === $paymentStatus ||
			MM_WPFS::PAYMENT_STATUS_AUTHORIZED === $paymentStatus ) {
			$refundLabel =
				/* translators: 'Refund' action label for payments */
				__( 'Refund', 'wp-full-stripe-free' );

			$html .= "<a class='wpfs-btn wpfs-btn-icon wpfs-btn-icon--20 js-tooltip js-refund-payment' data-tooltip-content='refund-tooltip'>
                <span class='wpfs-icon-sync'></span>
            </a>
            <div class='wpfs-tooltip-content' data-tooltip-id='refund-tooltip'>
                <div class='wpfs-info-tooltip'>{$refundLabel}</div>
            </div>";
		}

		$deleteLabel =
			/* translators: 'Delete' action label for payments */
			__( 'Delete payment', 'wp-full-stripe-free' );
		$html .= "<a class='wpfs-btn wpfs-btn-icon wpfs-btn-icon--20 js-tooltip js-delete-payment' data-tooltip-content='delete-payment-tooltip'>
                <span class='wpfs-icon-trash'></span>
            </a>
            <div class='wpfs-tooltip-content' data-tooltip-id='delete-payment-tooltip'>
                <div class='wpfs-info-tooltip'>{$deleteLabel}</div>
            </div>";

		$html .= '</td>';

		return $html;
	}

	public function display_rows() {
		$payments = $this->items;
		list( $columns, $hidden ) = $this->get_column_info();

		if ( ! empty( $payments ) ) {
			foreach ( $payments as $payment ) {
				$rowHtml = "<tr class='wpfs-data-table__tr' data-db-id='{$payment->paymentID}' data-stripe-id='{$payment->eventID}'>";

				foreach ( $columns as $key => $displayName ) {
					switch ( $key ) {
						case self::COLUMN_PAYMENT_METHOD:
							$rowHtml .= $this->renderPaymentMethodCell( $payment );
							break;

						case self::COLUMN_ID_DATE:
							$rowHtml .= $this->renderIdDateCell( $payment );
							break;

						case self::COLUMN_CUSTOMER_FORM:
							$rowHtml .= $this->renderCustomerFormCell( $payment );
							break;

						case self::COLUMN_AMOUNT:
							$rowHtml .= $this->renderAmountCell( $payment );
							break;

						case self::COLUMN_STATUS_MODE:
							$rowHtml .= $this->renderStatusModeCell( $payment );
							break;

						case self::COLUMN_ACTIONS:
							$rowHtml .= $this->renderActionsCell( $payment );
							break;
					}
				}

				$rowHtml .= "</tr>";
				echo $rowHtml;
			}
		}
	}
}


class WPFS_Base_Table extends WPFS_List_Table {

	const HTTPS_DASHBOARD_STRIPE_COM = "https://dashboard.stripe.com/";
	const PATH_TEST = "test/";
	const PATH_CUSTOMERS = 'customers/';
	const PATH_CHARGES = 'charges/';
	const PATH_PAYMENTS = 'payments/';
	const PATH_SUBSCRIPTIONS = 'subscriptions/';

	public function print_column_headers( $with_id = true ) {
		list( $columns, $hidden, $sortable, $primary ) = $this->get_column_info();

		$current_url = set_url_scheme( 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] );
		$current_url = remove_query_arg( 'paged', $current_url );

		if ( isset( $_GET['orderby'] ) ) {
			$current_orderby = $_GET['orderby'];
		} else {
			$current_orderby = '';
		}

		if ( isset( $_GET['order'] ) && 'desc' === $_GET['order'] ) {
			$current_order = 'desc';
		} else {
			$current_order = 'asc';
		}

		if ( ! empty( $columns['cb'] ) ) {
			static $cb_counter = 1;
			$columns['cb'] = '<label class="screen-reader-text" for="cb-select-all-' . $cb_counter . '">' .
				/* translators: Label for the 'Select all' option of status filter on list pages */
				__( 'Select All', 'wp-full-stripe-free' ) . '</label>'
				. '<input id="cb-select-all-' . $cb_counter . '" type="checkbox" />';
			$cb_counter++;
		}

		foreach ( $columns as $column_key => $column_display_name ) {
			$class = [ 'manage-column', "column-$column_key" ];

			if ( in_array( $column_key, $hidden ) ) {
				$class[] = 'hidden';
			}

			if ( 'cb' === $column_key ) {
				$class[] = 'check-column';
			} elseif ( in_array( $column_key, [ 'posts', 'comments', 'links' ] ) ) {
				$class[] = 'num';
			}

			if ( $column_key === $primary ) {
				$class[] = 'column-primary';
			}

			if ( isset( $sortable[ $column_key ] ) ) {
				list( $orderby, $desc_first ) = $sortable[ $column_key ];

				if ( $current_orderby === $orderby ) {
					$order = 'asc' === $current_order ? 'desc' : 'asc';
					$class[] = 'sorted';
					$class[] = $current_order;
				} else {
					$order = $desc_first ? 'desc' : 'asc';
					$class[] = 'sortable';
					$class[] = $desc_first ? 'asc' : 'desc';
				}

				if ( strpos( $column_display_name, '<br>' ) ) {
					$column_display_name_parts = explode( '<br>', $column_display_name );
					foreach ( $column_display_name_parts as $i => $part ) {
						if ( $i == 0 ) {
							$column_display_name = '<a href="' . esc_url( add_query_arg( compact( 'orderby', 'order' ), $current_url ) ) . '"><span>' . $part . '</span><span class="sorting-indicator"></span></a>';
						} else {
							$column_display_name .= '<span class="wpfs-table-sub-header">' . $part . '</span>';
						}
					}
				} else {
					$column_display_name = '<a href="' . esc_url( add_query_arg( compact( 'orderby', 'order' ), $current_url ) ) . '"><span>' . $column_display_name . '</span><span class="sorting-indicator"></span></a>';
				}
			}

			$tag = ( 'cb' === $column_key ) ? 'td' : 'th';
			$scope = ( 'th' === $tag ) ? 'scope="col"' : '';
			$id = $with_id ? "id='$column_key'" : '';

			if ( ! empty( $class ) ) {
				$class = "class='" . join( ' ', $class ) . "'";
			}

			echo "<$tag $scope $id $class>$column_display_name</$tag>";
		}
	}

	/**
	 * @param $title
	 *
	 * @param $aggregated_columns
	 *
	 * @return string
	 */
	protected function format_column_header_title( $title, array $aggregated_columns = null ) {
		$column_label = "<b>{$title}</b>";
		if ( ! empty( $aggregated_columns ) ) {
			$size = sizeof( $aggregated_columns );
			$column_label .= '<br>';
			foreach ( $aggregated_columns as $key => $value ) {
				$column_label .= $value;
				if ( $key < $size - 1 ) {
					$column_label .= ' / ';
				}
			}
		}

		return $column_label;
	}

	/**
	 * @param $stripe_customer_id
	 * @param $live_mode
	 *
	 * @return string
	 */
	protected function build_stripe_customer_link( $stripe_customer_id, $live_mode ) {
		$href = $this->build_stripe_base_url( $live_mode );
		$href .= self::PATH_CUSTOMERS . $stripe_customer_id;

		return $href;
	}

	/**
	 * @param $live_mode
	 *
	 * @return string
	 */
	protected function build_stripe_base_url( $live_mode ) {
		$href = self::HTTPS_DASHBOARD_STRIPE_COM;
		if ( $live_mode == 0 ) {
			$href .= self::PATH_TEST;
		}

		return $href;
	}

	/**
	 * @param $stripe_subscription_id
	 * @param $live_mode
	 *
	 * @return string
	 */
	protected function build_stripe_subscription_link( $stripe_subscription_id, $live_mode ) {
		$href = $this->build_stripe_base_url( $live_mode );
		$href .= self::PATH_SUBSCRIPTIONS . $stripe_subscription_id;

		return $href;
	}

	/**
	 * @param $stripe_charge_id
	 * @param $live_mode
	 *
	 * @return string
	 */
	protected function build_stripe_payments_link( $stripe_charge_id, $live_mode ) {
		$href = $this->build_stripe_base_url( $live_mode );
		$href .= self::PATH_PAYMENTS . $stripe_charge_id;

		return $href;
	}

	/**
	 * Add extra markup in the toolbars before or after the list
	 *
	 * @param string $which , helps you decide if you add the markup after (bottom) or before (top) the list
	 */
	protected function extra_tablenav( $which ) {
		if ( $which == "top" ) {
			echo '<div class="wrap">';
		}
		if ( $which == "bottom" ) {
			echo '</div>';
		}
	}

}

class WPFS_Log_Table extends WPFS_Base_Table {

	public function __construct() {
		parent::__construct( [
			'singular' => __( 'Log entry', 'wp-full-stripe-free' ),
			'plural' => __( 'Log entries', 'wp-full-stripe-free' ),
			'ajax' => false
		] );
	}

	/**
	 * Prepare the table with different parameters, pagination, columns and table elements
	 */
	public function prepare_items() {
		global $wpdb;

		$query = "SELECT * FROM {$wpdb->prefix}fullstripe_log";

		$where  = [];
		$params = [];

		$module = ! empty( $_REQUEST['module'] ) ? esc_sql( trim( $_REQUEST['module'] ) ) : null;
		$level = ! empty( $_REQUEST['level'] ) ? esc_sql( trim( $_REQUEST['level'] ) ) : null;

		if ( isset( $module ) ) {
			$where[] = "LOWER(module) = LOWER(%s)";
			$params[]         = $module;
		}

		if ( isset( $level ) ) {
			$where[] = "`level` = %d";
			$params[]         = $level;
		}

		if ( ! empty( $where ) ) {
			$query .= ' WHERE ' . implode( ' AND ', $where );
		}

		$orderBy = ! empty( $_REQUEST['orderby'] ) ? esc_sql( $_REQUEST['orderby'] ) : 'created';
		$order = ! empty( $_REQUEST['order'] ) ? esc_sql( $_REQUEST['order'] ) : ( empty( $_REQUEST['orderby'] ) ? 'DESC' : 'ASC' );
		if ( ! empty( $orderBy ) && ! empty( $order ) ) {
			$query .= ' ORDER BY ' . $orderBy . ' ' . $order;
		}

		$total_items = $wpdb->query( $wpdb->prepare( $query, ...$params ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
		$per_page = 50;
		$total_pages = ceil( $total_items / $per_page );
		$this->set_pagination_args( [
			"total_items" => $total_items,
			"total_pages" => $total_pages,
			"per_page" => $per_page,
		] );
		$current_page = $this->get_pagenum();
		if ( ! empty( $current_page ) && ! empty( $per_page ) ) {
			$offset = ( $current_page - 1 ) * $per_page;
			$query .= ' LIMIT ' . (int) $offset . ',' . (int) $per_page;
		}

		$columns = $this->get_columns();
		$hidden = [];
		$sortable = $this->get_sortable_columns();
		$this->_column_headers = [ $columns, $hidden, $sortable ];

		$this->items = $wpdb->get_results( $query );  // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
	}

	/**
	 * Decide which columns to activate the sorting functionality on
	 * @return array $sortable, the array of columns that can be sorted by the user
	 */
	protected function get_sortable_columns() {
		return [
			'created' => [ 'created', false ]
		];
	}


	/**
	 * Display the rows of records in the table
	 * @return string, echo the markup of the rows
	 */
	public function display_rows() {
		$items = $this->items;

		list( $columns, $hidden ) = $this->get_column_info();

		if ( ! empty( $items ) ) {
			foreach ( $items as $item ) {
				$row = '';
				$row .= "<tr id=\"record_{$item->id}\">";
				foreach ( $columns as $column_name => $column_display_name ) {
					$class = "class=\"$column_name column-$column_name\"";
					$style = "";
					if ( in_array( $column_name, $hidden ) ) {
						$style = " style=\"display:none;\"";
					}
					$attributes = "{$class} {$style}";

					switch ( $column_name ) {
						case 'created':
							$dateLabel = MM_WPFS_Utils::formatTimestampWithWordpressDateTimeFormat( strtotime( $item->created ) );
							$row .= "<td {$attributes}>{$dateLabel}</td>";
							break;
						case 'module':
							$module = $item->module;
							$row .= "<td {$attributes}>{$module}</td>";
							break;
						case 'class':
							$class = $item->class;
							$row .= "<td {$attributes}>{$class}</td>";
							break;
						case 'function':
							$function = $item->function;
							$row .= "<td {$attributes}>{$function}</td>";
							break;
						case 'level':
							$level = $item->level;
							$row .= "<td {$attributes}>{$level}</td>";
							break;
						case 'message':
							$message = $item->message;
							$stackTrace = $item->exception;
							$row .= "<td {$attributes} data-toggle=\"{$stackTrace}\">{$message}</td>";
							break;
					}
				}

				$row .= '</tr>';

				echo $row;
			}
		}
	}

	public function no_items() {
		_e( 'No log entries found.', 'wp-full-stripe-free' );
	}

}
