<?php

namespace wpforo\classes;

use stdClass;
use wpforo\admin\listtables\Boards as BoardsListTable;

// Exit if accessed directly
if( ! defined( 'ABSPATH' ) ) exit;

class Boards {
	public  $default;
	private $board;
	public  $list_table;
	public  $route       = 'community';
	public  $full_route  = 'community';
	public  $base_routes = [];
	public  $routes      = [];
	public  $is_inited   = [];
	
	public function __construct() {
		$this->init_defaults();
		$this->reset_current();
		$this->init_routes();
		$this->init_hooks();
		if( is_admin() ) add_action( 'wpforo_after_init', [ $this, 'init_list_table' ] );
	}
	
	public function init_hooks() {
		add_action( 'wpforo_after_add_board', function( $board ) {
			$this->after_save_board( $board['boardid'] );
		} );
		add_action( 'wpforo_after_edit_board', function( $boardid ) {
			$this->after_save_board( $boardid );
		} );
		add_filter( 'wpforo_settings_init_core_info', function( $settings_info ) {
			return array_filter( $settings_info, function( $v, $k ) {
				return $v['base'] || $this->is_module_enabled( $k );
			},                   ARRAY_FILTER_USE_BOTH );
		} );
		add_filter( 'wpforo_settings_init_addons_info', function( $settings_info ) {
			return array_filter( $settings_info, function( $v, $k ) {
				return $v['base'] || $this->is_module_enabled( $k );
			},                   ARRAY_FILTER_USE_BOTH );
		} );
	}
	
	public function init_list_table() {
		if( wpfval( $_GET, 'page' ) === 'wpforo-boards' ) {
			$this->list_table = new BoardsListTable();
			$this->list_table->prepare_items();
		}
	}
	
	private function init_defaults() {
		$this->default                  = new stdClass();
		$this->default->board           = [
			'boardid'       => 0,
			'title'         => 'Forums',
			'slug'          => 'community',
			'pageid'        => 0,
			'modules'       => [],
			'locale'        => 'en_US',
			'is_standalone' => false,
			'excld_urls'    => [],
			'status'        => true,
			'settings'      => [
				'title' => 'Forum',
				'desc'  => 'Discussion Board',
			],
		];
		$this->default->board_format    = [
			'boardid'       => '%d',
			'title'         => '%s',
			'slug'          => '%s',
			'pageid'        => '%d',
			'modules'       => '%s',
			'locale'        => '%s',
			'is_standalone' => '%d',
			'excld_urls'    => '%s',
			'status'        => '%d',
			'settings'      => '%s',
		];
		$this->default->sql_select_args = [
			'title_like'      => null,
			'title_notlike'   => null,
			'locale_like'     => null,
			'locale_notlike'  => null,
			'locale_empty'    => null,
			'is_standalone'   => null,
			'status'          => null,
			'slug_include'    => [],
			'slug_exclude'    => [],
			'boardid_include' => [],
			'boardid_exclude' => [],
			'pageid_include'  => [],
			'pageid_exclude'  => [],
			'orderby'         => null,
			'offset'          => null,
			'row_count'       => null,
		];
	}
	
	/**
	 * @param $board
	 *
	 * @return array
	 */
	public function decode( $board ) {
		$board                  = array_merge( $this->default->board, (array) $board );
		$board['boardid']       = intval( $board['boardid'] );
		$board['pageid']        = wpforo_bigintval( $board['pageid'] );
		$board['title']         = trim( strip_tags( (string) $board['title'] ) );
		$board['is_standalone'] = (bool) intval( $board['is_standalone'] );
		$board['status']        = (bool) intval( $board['status'] );
		$board['slug']          = sanitize_title( $board['slug'], 'community' );
		$board['locale']        = trim( strip_tags( (string) $board['locale'] ) );
		$board['excld_urls']    = array_values(
			array_unique(
				array_filter(
					(array) ( wpforo_is_json( $board['excld_urls'] ) ? json_decode( $board['excld_urls'], true ) : ( is_scalar(
						$board['excld_urls']
					) ? array_map(
						'trim',
						wpforo_string2array(
							sanitize_textarea_field(
								$board['excld_urls']
							)
						)
					) : $board['excld_urls'] ) )
				)
			)
		);
		
		// -- START -- decode board settings
		$board['settings']          = (array) ( wpforo_is_json( $board['settings'] ) ? json_decode(
			$board['settings'],
			true
		) : ( is_scalar( $board['settings'] ) ? array_map(
			'trim',
			wpforo_string2array(
				sanitize_textarea_field(
					$board['settings']
				)
			)
		) : $board['settings'] ) );
		$board['settings']          = wpforo_array_args_cast_and_merge( $board['settings'], $this->default->board['settings'] );
		$board['settings']['title'] = trim( strip_tags( (string) $board['settings']['title'] ) );
		$board['settings']['desc']  = trim( strip_tags( (string) $board['settings']['desc'] ) );
		// -- END -- decode board settings
		
		if( ! $board['locale'] ) $board['locale'] = wpforo_get_site_default_locale();
		if( ! $board['pageid'] ) $board['pageid'] = wpforo_get_option( 'wpforo_pageid', 0 );
		
		$all_modules      = array_map( '__return_true', wpforo_get_modules_info() );
		$all_addons       = array_map( '__return_true', wpforo_get_addons_info() );
		$modules          = (array) ( ( wpforo_is_json( $board['modules'] ) ) ? json_decode(
			$board['modules'],
			true
		) : $board['modules'] );
		$modules          = array_merge( $all_modules, $all_addons, $modules );
		$board['modules'] = array_map( function( $a ) { return (bool) intval( $a ); }, $modules );
		
		return $board;
	}
	
	/**
	 * @param $board
	 *
	 * @return array
	 */
	private function encode( $board ) {
		$board                  = $this->decode( $board );
		$board['modules']       = wp_json_encode( $board['modules'] );
		$board['excld_urls']    = wp_json_encode( $board['excld_urls'] );
		$board['settings']      = wp_json_encode( $board['settings'] );
		$board['is_standalone'] = intval( $board['is_standalone'] );
		$board['status']        = intval( $board['status'] );
		
		return $board;
	}
	
	/**
	 * @param $board
	 *
	 * @return false|int
	 */
	public function add( $board ) {
		$board = $this->encode( $board );
		unset( $board['boardid'] );
		$board = wpforo_array_ordered_intersect_key( $board, $this->default->board_format );
		if( WPF()->db->insert(
			WPF()->tables->boards,
			$board,
			wpforo_array_ordered_intersect_key( $this->default->board_format, $board )
		) ) {
			flush_rewrite_rules( false );
			nocache_headers();
			
			$board['boardid'] = WPF()->db->insert_id;
			do_action( 'wpforo_after_add_board', $board );
			
			return $board['boardid'];
		}
		
		return false;
	}
	
	/**
	 * @param $fields
	 * @param $boardid
	 *
	 * @return bool
	 */
	public function edit( $fields, $boardid ) {
		$boardid = intval( $boardid );
		$fields  = wpforo_array_ordered_intersect_key( $fields, $this->default->board_format );
		if( false !== WPF()->db->update(
				WPF()->tables->boards,
				$data = wpforo_array_ordered_intersect_key( $this->encode( $fields ), $fields ),
				[ 'boardid' => $boardid ],
				wpforo_array_ordered_intersect_key( $this->default->board_format, $fields ),
				[ '%d' ]
			) ) {
			flush_rewrite_rules( false );
			nocache_headers();
			
			do_action( 'wpforo_after_edit_board', $boardid, $data );
			
			return true;
		}
		
		return false;
	}
	
	/**
	 * @param $boardid
	 *
	 * @return bool
	 */
	public function delete( $boardid ) {
		do_action( 'wpforo_before_delete_board', $boardid );
		
		if( false !== WPF()->db->delete( WPF()->tables->boards, [ 'boardid' => $boardid ], [ '%d' ] ) ) {
			flush_rewrite_rules( false );
			nocache_headers();
			
			do_action( 'wpforo_after_delete_board', $boardid );
			
			return true;
		}
		
		return false;
	}
	
	private function after_save_board( $boardid ) {
		if( $board = $this->_get_board( (int) $boardid ) ) {
			if( $board['status'] && $board['is_standalone'] ) {
				$sql = "UPDATE `" . WPF(
					)->tables->boards . "` SET `is_standalone` = 0 WHERE `is_standalone` = 1 AND `boardid` <> %d";
				WPF()->db->query( WPF()->db->prepare( $sql, $board['boardid'] ) );
			}
		}
	}
	
	private function parse_args( $args ): array {
		$args                    = wpforo_parse_args( $args, $this->default->sql_select_args );
		$args                    = wpforo_array_ordered_intersect_key( $args, $this->default->sql_select_args );
		$args['slug_include']    = wpforo_parse_args( $args['slug_include'] );
		$args['slug_exclude']    = wpforo_parse_args( $args['slug_exclude'] );
		$args['boardid_include'] = wpforo_parse_args( $args['boardid_include'] );
		$args['boardid_exclude'] = wpforo_parse_args( $args['boardid_exclude'] );
		$args['pageid_include']  = wpforo_parse_args( $args['pageid_include'] );
		$args['pageid_exclude']  = wpforo_parse_args( $args['pageid_exclude'] );
		
		return $args;
	}
	
	private function build_sql_select( $args, $select = '' ): string {
		$args = $this->parse_args( $args );
		if( ! $select ) $select = '*';
		
		$wheres = [];
		
		if( ! is_null( $args['title_like'] ) ) $wheres[] = "`title` LIKE '%" . WPF()->db->esc_like( $args['title_like'] ) . "%'";
		if( ! is_null( $args['title_notlike'] ) ) {
			$wheres[] = "`title` NOT LIKE '%" . WPF()->db->esc_like(
					$args['title_notlike']
				) . "%'";
		}
		
		if( ! is_null( $args['locale_like'] ) ) {
			$locale_like = "`locale` LIKE '%" . WPF()->db->esc_like( $args['locale_like'] ) . "%'";
			if( $args['locale_empty'] ) {
				$locale_like = "( `locale` = '' OR `locale` IS NULL OR " . $locale_like . " )";
			}
			$wheres[] = $locale_like;
		}
		if( ! is_null( $args['locale_notlike'] ) ) {
			$locale_notlike = "`locale` NOT LIKE '%" . WPF()->db->esc_like( $args['locale_notlike'] ) . "%'";
			if( ! is_null( $args['locale_empty'] ) ) {
				if( $args['locale_empty'] ) {
					$locale_notlike = "( `locale` = '' OR `locale` IS NULL OR " . $locale_notlike . " )";
				} else {
					$locale_notlike = "( `locale` <> '' AND `locale` IS NOT NULL AND " . $locale_notlike . " )";
				}
			}
			$wheres[] = $locale_notlike;
		}
		if( ! is_null( $args['locale_empty'] ) && is_null( $args['locale_like'] ) && is_null( $args['locale_notlike'] ) ) {
			if( $args['locale_empty'] ) {
				$wheres[] = "( `locale` = '' OR `locale` IS NULL )";
			} else {
				$wheres[] = "( `locale` <> '' AND `locale` IS NOT NULL )";
			}
		}
		
		if( ! is_null( $args['status'] ) ) $wheres[] = "`status` = '" . intval( $args['status'] ) . "'";
		if( ! is_null( $args['is_standalone'] ) ) $wheres[] = "`is_standalone` = '" . intval( $args['is_standalone'] ) . "'";
		
		if( ! empty( $args['slug_include'] ) ) {
			$wheres[] = "`slug` IN('" . implode(
					"','",
					array_map(
						function( $_i ) {
							return esc_sql(
								trim( $_i )
							);
						},
						$args['slug_include']
					)
				) . "')";
		}
		if( ! empty( $args['slug_exclude'] ) ) {
			$wheres[] = "`slug` NOT IN(" . implode(
					"','",
					array_map(
						function( $_i ) {
							return esc_sql(
								trim( $_i )
							);
						},
						$args['slug_exclude']
					)
				) . "')";
		}
		
		if( ! empty( $args['boardid_include'] ) ) {
			$wheres[] = "`boardid` IN(" . implode(
					',',
					array_map(
						'intval',
						$args['boardid_include']
					)
				) . ")";
		}
		if( ! empty( $args['boardid_exclude'] ) ) {
			$wheres[] = "`boardid` NOT IN(" . implode(
					',',
					array_map(
						'intval',
						$args['boardid_exclude']
					)
				) . ")";
		}
		
		if( ! empty( $args['pageid_include'] ) ) {
			$wheres[] = "`pageid` IN(" . implode(
					',',
					array_map(
						'wpforo_bigintval',
						$args['pageid_include']
					)
				) . ")";
		}
		if( ! empty( $args['pageid_exclude'] ) ) {
			$wheres[] = "`pageid` NOT IN(" . implode(
					',',
					array_map(
						'wpforo_bigintval',
						$args['pageid_exclude']
					)
				) . ")";
		}
		
		$wheres = array_filter( $wheres );
		
		$sql = "SELECT $select FROM " . WPF()->tables->boards;
		if( $wheres ) $sql .= " WHERE " . implode( " AND ", $wheres );
		if( $args['orderby'] ) $sql .= " ORDER BY " . $args['orderby'];
		if( $args['row_count'] ) $sql .= " LIMIT " . intval( $args['offset'] ) . "," . intval( $args['row_count'] );
		
		return $sql;
	}
	
	/**
	 * @param array|numeric $args
	 *
	 * @return array
	 */
	public function _get_board( $args ) {
		if( wpforo_is_id( $args ) ) $args = [ 'boardid_include' => (int) $args ];
		if( ! wpfkey( $args, 'orderby' ) ) $args['orderby'] = '`boardid` DESC';
		if( ! wpfkey( $args, 'row_count' ) ) {
			$args['offset']    = 0;
			$args['row_count'] = 1;
		}
		$board = (array) WPF()->db->get_row( $this->build_sql_select( $args ), ARRAY_A );
		if( $board ) $board = $this->decode( $board );
		
		return $board;
	}
	
	/**
	 * @param array|numeric $args
	 *
	 * @return array
	 */
	public function get_board( $args ) {
		return wpforo_ram_get( [ $this, '_get_board' ], $args );
	}
	
	/**
	 * @param array $args
	 *
	 * @return array
	 */
	public function _get_boards( $args = [] ) {
		return array_map( [ $this, 'decode' ], (array) WPF()->db->get_results( $this->build_sql_select( $args ), ARRAY_A ) );
	}
	
	public function get_boards( $args = [] ) {
		return wpforo_ram_get( [ $this, '_get_boards' ], $args );
	}
	
	/**
	 * @param array $args
	 *
	 * @return int
	 */
	public function get_count( $args = [] ) {
		return (int) WPF()->db->get_var( $this->build_sql_select( $args, 'COUNT(*)' ) );
	}
	
	public function reset_current() {
		if( WPF()->is_installed() && ( $board0 = $this->get_board( 0 ) ) ) {
			$this->board = $board0;
		} else {
			$this->board = $this->default->board;
		}
	}
	
	private function init_routes() {
		$this->base_routes = array_unique( array_merge( $this->base_routes, array_keys( WPF()->tpl->base_templates ) ) );
		$this->base_routes = array_map( 'wpforo_settings_get_slug', $this->base_routes );
		$slugs             = [];
		if( WPF()->is_installed() ) {
			$boards = $this->get_boards( [ 'status' => true, 'orderby' => '`boardid` ASC' ] );
			foreach( $boards as $board ) $slugs[] = $board['slug'];
			if( $slug0 = wpfval( $slugs, 0 ) ) $this->route = $this->full_route = $slug0;
		}
		$this->routes = array_unique( array_merge( $this->base_routes, $slugs ) );
		if( ! $this->routes ) $this->routes = (array) $this->route;
		$this->routes = preg_replace( '#^/?index\.php/?#iu', '', $this->routes );
		$this->routes = array_map( function( $route ) { return trim( (string) $route, '/' ); }, $this->routes );
	}
	
	/**
	 * @param array $args
	 */
	public function init_current( $args = [] ) {
		if( ! WPF()->is_installed() ) return;
		$get = wp_unslash( array_merge( WPF()->GET, $_GET ) );
		if( is_numeric( $args ) ) $args = [ 'boardid' => intval( $args ) ];
		$board = [];
		if( is_null( $boardid = wpfval( $args, 'boardid' ) ) && is_null( $boardid = wpfval( $get, 'boardid' ) ) && is_null(
				$boardid = wpfval( $get, 'wpforo_boardid' )
			) ) {
			if( wpforo_is_admin() ) {
				if( preg_match( '#^wpforo-(?:(\d*)-)?#iu', (string) wpfval( $get, 'page' ), $match ) ) {
					if( ! $boardid = (int) wpfval( $get, 'boardid' ) ) {
						$boardid = (int) wpfval( $match, 1 );
					}
				}
			} else {
				if( $slug = (string) wpfval( $args, 'slug' ) ) {
					$args  = [
						'slug_include' => $slug,
						'locale_like'  => ( $locale = (string) wpfval(
							$args,
							'locale'
						) ) ? $locale : wpforo_get_site_default_locale(),
						'locale_empty' => true,
						'status'       => true,
						'orderby'      => "( `locale` = '' OR `locale` IS NULL ) ASC, `boardid` DESC",
					];
					$board = $this->get_board( $args );
				}
				if( ! $board && $pageid = get_the_ID() ) {
					$args = [
						'pageid_include' => $pageid,
						'status'         => true,
						'orderby'        => "( `locale` = '' OR `locale` IS NULL ) ASC, `boardid` DESC",
					];
					if( $slug = wpforo_get_url_route() ) $args['slug_include'] = $slug;
					$board = $this->get_board( $args );
				}
				if( ! $board && $slug = wpforo_get_url_route() ) {
					$args  = [
						'slug_include' => $slug,
						'locale_like'  => ( $locale = (string) wpfval(
							$args,
							'locale'
						) ) ? $locale : wpforo_get_site_default_locale(),
						'locale_empty' => true,
						'status'       => true,
						'orderby'      => "( `locale` = '' OR `locale` IS NULL ) ASC, `boardid` DESC",
					];
					$board = $this->get_board( $args );
				}
				if( ! $board ) {
					$args  = [
						'is_standalone' => true,
						'status'        => true,
						'orderby'       => "( `locale` = '' OR `locale` IS NULL ) ASC, `boardid` DESC",
					];
					$board = $this->get_board( $args );
				}
			}
		}
		
		if( $board || ( $board = $this->get_board( intval( $boardid ) ) ) ) {
			$this->board = $board;
		} else {
			$this->board = $this->default->board;
		}
		
		$this->board = apply_filters( 'wpforo_board_init_current', $this->board );
		
		if( ! $this->board['locale'] ) $this->board['locale'] = wpforo_get_site_default_locale();
		
		if( ! $this->board['pageid'] ) $this->board['pageid'] = wpforo_get_option( 'wpforo_pageid', 0 );
		if( ! wpforo_is_admin() && $this->board['locale'] && WPF()->locale !== $this->board['locale'] ) {
			if( is_wpforo_url() && is_wpforo_multiboard() ) switch_to_locale( $this->board['locale'] );
		}
		
		$this->route      = ( ! $this->board['is_standalone'] ? $this->board['slug'] : '' );
		$this->full_route = rtrim(
			( strpos(
				  (string) get_option( 'permalink_structure', '' ),
				  'index.php'
			  ) !== false ? '/index.php/' : '/' ) . $this->route,
			'/\\'
		);
		## @TODO need to test with or without lang plugins
		//		if( $lang = wpforo_get_query_var_lang() ) $this->full_route = "/$lang" . $this->full_route;
		
		load_plugin_textdomain( 'wpforo', false, basename( WPFORO_DIR ) . '/languages' );
		
		$this->is_inited[ $this->board['boardid'] ] = true;
	}
	
	/**
	 * @param string|null $field
	 *
	 * @return mixed
	 */
	public function get_current( $field = null ) {
		if( is_null( $field ) ) return $this->board;
		
		return wpfval( $this->board, $field );
	}
	
	/**
	 * @param string $module_key
	 * @param array $board
	 *
	 * @return bool
	 */
	public function is_module_enabled( string $module_key, array $board = [] ): bool {
		if( ! $board ) $board = $this->get_current();
		
		return ! wpfkey( $board, 'modules', $module_key ) || wpfval( $board, 'modules', $module_key );
	}
	
	public function is_multi(): bool {
		return 1 < $this->get_count( [ 'status' => true ] );
	}
	
	public function get_active_boardids(): array {
		$boardids = [];
		$boards   = $this->get_boards( [ 'status' => true ] );
		foreach( $boards as $board ) $boardids[] = $board['boardid'];
		
		return $boardids;
	}
	
	public function dropdown( $selected = null ): string {
		if( is_null( $selected ) ) $selected = $this->get_current( 'boardid' );
		$selected = intval( $selected );
		
		$html = '';
		foreach( $this->get_boards( [ 'status' => true ] ) as $board ) {
			$html .= sprintf(
				'<option value="%1$d" %2$s>%3$s</option>',
				$board['boardid'],
				selected( $board['boardid'], $selected, false ),
				$board['title'] . '(' . strtok( $board['locale'], '_' ) . ')'
			);
		}
		
		return $html;
	}
	
	public function get_boards_pageids(): array {
		$pageids = [];
		foreach( $this->get_boards() as $board ) $pageids[] = $board['pageid'];
		
		return array_unique( $pageids );
	}
}
