<?php
namespace BwWinnersNetwork\Query;

if ( ! class_exists( __NAMESPACE__ . 'Abstract_Query' ) ) {

	abstract class Abstract_Query {

		/** @var array */
		protected $select = [];

		/** @var string */
		protected $from = '';

		/** @var array */
		protected $where = [];

		/** @var string */
		protected $order_by = '';

		/** @var string */
		protected $limit = '';

		/** @var string */
		protected $query = '';

		/** @var string */
		protected $count_query = '';

		/** @var array */
		protected $results = [];

		/** @var int */
		protected $page = 1;

		/** @var int */
		protected $total_pages = 1;

		/** @var int */
		protected $per_page = 20;

		/** @var int */
		protected $site_id;

		/** @var \wpdb */
		protected $wpdb;

		public function __construct( $args = [] ) {
			global $wpdb;
			$this->wpdb = $wpdb;
			$this->site_id = $args['global_site_id'] ?? get_current_blog_id();

			$this->page     = max( intval( $args['page'] ?? 1 ), 1 );
			$this->per_page = intval( $args['per_page'] ?? 20 );

			$this->build( $args );
		}

		/** -------------------------
		 *  Methods subclasses MUST implement
		 *  ------------------------- */
		abstract protected function build_select( $args );
		abstract protected function build_from( $args );
		abstract protected function apply_filters( $args );
		abstract protected function apply_ordering( $args );

		/** -------------------------
		 *  Core builder
		 *  ------------------------- */
		protected function build( $args ) {

			if ( $this->per_page === 0 && $this->page > 1 ) {
				return;
			}

			$this->build_select( $args );
			$this->build_from( $args );
			$this->apply_filters( $args );
			$this->apply_ordering( $args );
			$this->apply_limit();
			$this->run();
		}

		protected function add_where( $sql ) {
			$this->where[] = $sql;
		}

		protected function compile_where() {
			return $this->where ? implode( " AND\n", $this->where ) : '';
		}

		protected function apply_limit() {
			if ( $this->per_page > 0 ) {
				$offset = ( $this->page - 1 ) * $this->per_page;
				$this->limit = $this->wpdb->prepare( "%d, %d", [ $offset, $this->per_page ] );
			}
		}

		protected function run() {

			$where = $this->compile_where();

			$this->query = sprintf(
				"SELECT %s FROM %s%s%s%s",
				implode( ",\n", $this->select ),
				$this->from,
				$where ? " WHERE {$where}" : '',
				$this->order_by ? " ORDER BY {$this->order_by}" : '',
				$this->limit ? " LIMIT {$this->limit}" : ''
			);

			// Count query
			if ( $this->per_page > 0 ) {
				$this->count_query = "SELECT COUNT(*) FROM {$this->from}";
				if ( $where ) {
					$this->count_query .= " WHERE {$where}";
				}

				$count = intval( $this->wpdb->get_var( $this->count_query ) );
				$this->total_pages = ceil( $count / $this->per_page );
			}

			$this->results = $this->wpdb->get_results( $this->query, ARRAY_A );
		}

		/** -------------------------
		 *  Public API
		 *  ------------------------- */
		public function get_results() {
			return $this->results;
		}

		public function get_page() {
			return $this->page;
		}

		public function get_total_pages() {
			return $this->total_pages;
		}

		public function get_query() {
			return $this->query;
		}

		public function get_count_query() {
			return $this->count_query;
		}
	}
}
