<?php

namespace GP_Entry_Blocks\Blocks\Entries_Table;

use GFAPI;
use function GP_Entry_Blocks\cleaned_current_url;
use function GP_Entry_Blocks\get_current_url;
use function GP_Entry_Blocks\get_edit_link;
use function GP_Entry_Blocks\get_field_display_value;
use function GP_Entry_Blocks\get_view_link;
use function GP_Entry_Blocks\get_delete_link;
use function GP_Entry_Blocks\get_duplicate_link;
use function GP_Entry_Blocks\get_view_url;
use function GP_Entry_Blocks\replace_merge_tags;

/**
 * Class for generating tables with the ability to easily add new column types, handle sorting, and whatever else we throw at it.
 *
 * Currently, used exclusively by the Entries Table block.
 *
 * @see \GP_Entry_Blocks\Blocks\Entries_Table
 */
class Table {
	/**
	 * @var Row[]
	 */
	public $rows;

	/**
	 * @var Column[]
	 */
	public $columns;

	/**
	 * @param $rows Row[]
	 * @param $columns Column[]
	 */
	public function __construct( $rows, $columns ) {
		$this->rows    = $rows;
		$this->columns = $columns;

		add_filter( 'gpeb_table_row_cell_content', array( $this, 'maybe_link_to_entry' ), 10, 3 );
	}


	/**
	 * Wraps the contents of the cell in a link to the entry if the column is configured to do so.
	 *
	 * @param string $cell_content
	 * @param Column $column
	 * @param Row $row
	 */
	public function maybe_link_to_entry( $cell_content, $column, $row ) {
		if ( ! rgar( $column->meta, 'linkToEntry' ) ) {
			return $cell_content;
		}

		$view_url = esc_attr( get_view_url( $row->entry ) );
		return "<a href='{$view_url}'>{$cell_content}</a>";
	}

	/**
	 * Output the table contents.
	 *
	 * @return string
	 */
	public function render() {
		$output = $this->render_header();

		$output .= '<tbody>';

		foreach ( $this->rows as $row ) {
			$output .= $this->render_row( $row );
		}

		$output .= '</tbody>';

		return $output;
	}

	/**
	 * @return string
	 */
	public function render_header() {
		$output = '<thead><tr>';

		foreach ( $this->columns as $column ) {
			$custom_label        = rgar( $column->meta, 'label' );
			$column_label_method = 'column_label_' . str_replace( '-', '_', $column->type );

			if ( ! rgblank( $custom_label ) ) {
				$label = $custom_label;
			} elseif ( method_exists( $this, $column_label_method ) ) {
				$label = call_user_func( array( $this, $column_label_method ), $column );
			} else {
				$label = $this->column_label_default( $column );
			}

			$output .= $this->render_column_header( $label, $column );
		}

		$output .= '</tr></thead>';

		return $output;
	}

	/**
	 * @param $row Row
	 *
	 * @return string
	 */
	public function render_row( $row ) {
		$output = '<tr>';

		foreach ( $this->columns as $column ) {
			$row_method = 'row_' . str_replace( '-', '_', $column->type );

			if ( method_exists( $this, $row_method ) ) {
				$cell_content = call_user_func( array( $this, $row_method ), $column, $row );
			} else {
				$cell_content = $this->row_default( $column, $row );
			}

			/**
			 * Filter the content of an individual cell in the Entries Table block.
			 *
			 * Note: This is the content within the <td> tag, not the entire <td> tag.
			 *
			 * @param string $cell_content The cell content.
			 * @param Column $column The current column.
			 * @param Row $row The current row.
			 */
			$cell_content = apply_filters( 'gpeb_table_row_cell_content', $cell_content, $column, $row );

			$markup = "<td class='" . $column->get_css_classes() . "'>{$cell_content}</td>";

			/**
			 * Filter the final markup of an individual cell in the Entries Table block.
			 *
			 * Note: This includes the entire <td> tag.
			 *
			 * @param string $cell_content The cell content.
			 * @param Column $column The current column.
			 * @param Row $row The current row.
			 */
			$output .= apply_filters( 'gpeb_table_row_cell_markup', $markup, $column, $row );
		}

		$output .= '</tr>';

		return $output;
	}

	/**
	 * @param $label  string Already-determined column label.
	 * @param $column Column Current column.
	 *
	 * @return string
	 */
	public function render_column_header( $label, $column ) {
		$output = '<th class="' . $column->get_css_classes() . '">';

		if ( $label && rgar( $column->meta, 'allowSorting' ) && $column->sorting_id ) {
			$active = (string) rgget( 'order_by' ) === (string) $column->sorting_id;

			$sorting_indicator_classes = array(
				'gpeb-sorting-indicator',
			);

			if ( $active ) {
				$sorting_indicator_classes[] = 'gpeb-sorting-indicator-active';

				if ( rgget( 'order' ) === 'desc' ) {
					$sorting_indicator_classes[] = 'gpeb-sorting-indicator-desc';
				}
			}

			$sorting_indicator_classes = implode( ' ', $sorting_indicator_classes );

			if ( $active && rgget( 'order' ) === 'desc' ) {
				$url = cleaned_current_url();
			} else {
				$url = add_query_arg( array(
					'order_by' => $column->sorting_id,
					'order'    => $active && rgget( 'order' ) === 'asc' ? 'desc' : 'asc',
				), get_current_url() );
			}

			$output .= "<a href='{$url}' class='gpeb-sorting-trigger'>{$label}<span class='{$sorting_indicator_classes}'></span></a>";
		} else {
			$output .= $label;
		}

		$output .= '</th>';

		return $output;
	}

	/**
	 * @param $column Column
	 *
	 * @return string
	 */
	public function column_label_default( $column ) {
		return '';
	}

	/**
	 * @param $column Column
	 *
	 * @return string
	 */
	public function column_label_id( $column ) {
		return esc_html__( 'ID', 'gp-entry-blocks' );
	}

	/**
	 * @param $column Column
	 *
	 * @return string
	 */
	public function column_label_date_created( $column ) {
		return esc_html__( 'Date Created', 'gp-entry-blocks' );
	}

	/**
	 * @param $column Column
	 *
	 * @return string
	 */
	public function column_label_date_updated( $column ) {
		return esc_html__( 'Date Updated', 'gp-entry-blocks' );
	}

	/**
	 * @param $column Column
	 *
	 * @return string
	 */
	public function column_label_ip( $column ) {
		return esc_html__( 'IP', 'gp-entry-blocks' );
	}


	/**
	 * @param $column Column
	 *
	 * @return string
	 */
	public function column_label_transaction_id( $column ) {
		return esc_html__( 'Transaction ID', 'gp-entry-blocks' );
	}

	/**
	 * @param $column Column
	 *
	 * @return string
	 */
	public function column_label_created_by( $column ) {
		return esc_html__( 'Created By', 'gp-entry-blocks' );
	}

	/**
	 * @param $column Column
	 *
	 * @return string
	 */
	public function column_label_payment_method( $column ) {
		return esc_html__( 'Payment Method', 'gp-entry-blocks' );
	}

	/**
	 * @param $column Column
	 *
	 * @return string
	 */
	public function column_label_payment_status( $column ) {
		return esc_html__( 'Payment Status', 'gp-entry-blocks' );
	}

	/**
	 * @param $column Column
	 *
	 * @return string
	 */
	public function column_label_field( $column ) {
		$field_id = rgar( $column->meta, 'fieldId' );
		$field    = GFAPI::get_field( rgar( $column->meta, 'form' ), $field_id );

		// If the field ID is actually an input ID, we need to get the input label.
		if ( strpos( $field_id, '.' ) !== false && ! empty( $field->inputs ) ) {
			foreach ( $field->inputs as $input ) {
				if ( $input['id'] == $field_id ) {
					return $input['label'];
				}
			}
		}

		return $field->label;
	}

	/**
	 * @param $column Column
	 *
	 * @return string
	 */
	public function column_label_meta( $column ) {
		$meta_key   = rgar( $column->meta, 'metaKey' );
		$entry_meta = \GFFormsModel::get_entry_meta( rgars( $column->meta, 'form/id' ) );

		return rgars( $entry_meta, $meta_key . '/label', '' );
	}

	/**
	 * @param $column Column Associated column.
	 * @param $row Row The current row.
	 *
	 * @return string
	 */
	public function row_default( $column, $row ) {
		return rgar( $row->entry, str_replace( '-', '_', $column->type ) );
	}

	/**
	 * @param $column Column Associated column.
	 * @param $row Row The current row.
	 *
	 * @return string
	 */
	public function row_field( $column, $row ) {
		$form_field_id = rgar( $column->meta, 'fieldId' );
		$field         = \GFAPI::get_field( $row->entry_form, $form_field_id );

		if ( ! $field ) {
			return $this->row_default( $column, $row );
		}

		return get_field_display_value( $field, $form_field_id, $row->entry, $row->entry_form );
	}

	/**
	 * @param $column Column Associated column.
	 * @param $row Row The current row.
	 *
	 * @return string
	 */
	public function row_meta( $column, $row ) {
		return rgar( $row->entry, rgar( $column->meta, 'metaKey' ) );
	}

	/**
	 * @param $column Column Associated column.
	 * @param $row Row The current row.
	 *
	 * @return string
	 */
	public function row_created_by( $column, $row ) {
		$user_id = rgar( $row->entry, 'created_by' );
		$user    = get_user_by( 'id', $user_id );

		return $user && ! is_wp_error( $user ) ? $user->display_name : '';
	}

	/**
	 * @param $column Column Associated column.
	 * @param $row Row The current row.
	 *
	 * @return string
	 */
	public function row_custom( $column, $row ) {
		$content = rgars( $column->meta, 'content' );

		return replace_merge_tags( $content, $row->entry_form, $row->entry );
	}

	/**
	 * @param $column Column Associated column.
	 * @param $row Row The current row.
	 *
	 * @return string
	 */
	public function row_edit_link( $column, $row ) {
		$link_text = rgars( $column->meta, 'linkLabel' );

		return get_edit_link( $row->entry, $link_text );
	}

	/**
	 * @param $column Column Associated column.
	 * @param $row Row The current row.
	 *
	 * @return string
	 */
	public function row_view_link( $column, $row ) {
		$link_text = rgars( $column->meta, 'linkLabel' );

		return get_view_link( $row->entry, $link_text );
	}

	/**
	 * @param $column Column Associated column.
	 * @param $row Row The current row.
	 *
	 * @return string
	 */
	public function row_delete_link( $column, $row ) {
		$link_text = rgars( $column->meta, 'linkLabel' );

		return get_delete_link( $row->entry, $link_text );
	}

	/**
	 * @param $column Column Associated column.
	 * @param $row Row The current row.
	 *
	 * @return string
	 */
	public function row_duplicate_link( $column, $row ) {
		$link_text = rgars( $column->meta, 'linkLabel' );

		return get_duplicate_link( $row->entry, $link_text );
	}
}
