<?php

namespace CelerSearch\Repositories;

use CelerSearch\DataTransfer\ViewConfig;
use CelerSearch\Interfaces\IRepository;
use function CelerSearch\Vendor\wp_query_builder;

/**
 * Repository for saved search view configurations
 */
class ViewRepository implements IRepository {

	/**
	 * Retrieve specific record from the database
	 *
	 * @param int $id
	 *
	 * @return ViewConfig|null
	 */
	public function find( int $id ): ?ViewConfig {
		global $wpdb;
		$table_name = $this->get_table_name();
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter -- Custom table
		$row = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $table_name WHERE id=%d", $id ) );

		if ( null === $row ) {
			return null;
		}

		return $this->prepare( $row );
	}

	/**
	 * Store specific record in the database
	 *
	 * @param array $data
	 *
	 * @return ViewConfig|null
	 */
	public function create( array $data ): ?ViewConfig {
		global $wpdb;

		$table_name = $this->get_table_name();

		$row    = [];
		$format = [];

		if ( isset( $data['name'] ) ) {
			$row['name'] = $data['name'];
			$format[]    = '%s';
		}

		if ( isset( $data['type'] ) ) {
			$row['type'] = $data['type'];
			$format[]    = '%s';
		} else {
			$row['type'] = 'search';
			$format[]    = '%s';
		}

		if ( isset( $data['index_id'] ) ) {
			$row['index_id'] = (int) $data['index_id'];
			$format[]        = '%d';
		}

		if ( isset( $data['config'] ) ) {
			$row['config'] = is_string( $data['config'] ) ? $data['config'] : wp_json_encode( $data['config'] );
			$format[]      = '%s';
		} else {
			$row['config'] = '{}';
			$format[]      = '%s';
		}

		$row['created_at'] = gmdate( 'Y-m-d H:i:s' );
		$format[]          = '%s';

		$row['updated_at'] = gmdate( 'Y-m-d H:i:s' );
		$format[]          = '%s';

		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery -- Custom table
		$result = $wpdb->insert( $table_name, $row, $format );

		if ( $result ) {
			return $this->find( $wpdb->insert_id );
		}

		return null;
	}

	/**
	 * Update specific record in the database
	 *
	 * @param int $id
	 * @param array $data
	 *
	 * @return ViewConfig|null
	 */
	public function update( int $id, array $data ): ?ViewConfig {
		global $wpdb;
		$table_name = $this->get_table_name();

		$row    = [];
		$format = [];

		if ( isset( $data['name'] ) ) {
			$row['name'] = $data['name'];
			$format[]    = '%s';
		}

		if ( isset( $data['type'] ) ) {
			$row['type'] = $data['type'];
			$format[]    = '%s';
		}

		if ( isset( $data['index_id'] ) ) {
			$row['index_id'] = (int) $data['index_id'];
			$format[]        = '%d';
		}

		if ( isset( $data['config'] ) ) {
			$row['config'] = is_string( $data['config'] ) ? $data['config'] : wp_json_encode( $data['config'] );
			$format[]      = '%s';
		}

		$row['updated_at'] = gmdate( 'Y-m-d H:i:s' );
		$format[]          = '%s';

		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Custom table
		$result = $wpdb->update( $table_name, $row, [ 'id' => $id ], $format, [ '%d' ] );

		// $wpdb->update returns false on error, or the number of rows affected (0 if no changes)
		if ( $result !== false ) {
			return $this->find( $id );
		}

		return null;
	}

	/**
	 * Delete specific record in the database
	 *
	 * @param int $id
	 *
	 * @return bool
	 */
	public function delete( int $id ): bool {
		global $wpdb;
		$table_name = $this->get_table_name();
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Custom table
		$result = $wpdb->delete( $table_name, [ 'id' => $id ], [ '%d' ] );
		return $result !== false && $result > 0;
	}

	/**
	 * Prepare a row
	 *
	 * @param object $row
	 *
	 * @return ViewConfig
	 */
	public function prepare( object $row ): ViewConfig {
		if ( isset( $row->id ) ) {
			$row->id = (int) $row->id;
		}

		if ( isset( $row->index_id ) ) {
			$row->index_id = (int) $row->index_id;
		}

		return new ViewConfig( $row );
	}

	/**
	 * Returns the table name
	 *
	 * @return string
	 */
	protected function get_table_name(): string {
		global $wpdb;

		return $wpdb->prefix . 'celersearch_views';
	}

	/**
	 * Retrieve multiple records from the database
	 *
	 * @param array $query
	 * @param int $page
	 * @param int $per_page
	 * @param string $order
	 * @param string $orderBy
	 *
	 * @return ViewConfig[]
	 * @throws \Exception
	 */
	public function get( array $query = [], int $page = 1, int $per_page = 10, string $order = 'DESC', string $orderBy = 'created_at' ): array {
		$offset = ( $page - 1 ) * $per_page;

		$records = wp_query_builder()
			->select( '*' )
			->from( 'celersearch_views' )
			->where( $query )
			->limit( $per_page )
			->offset( $offset )
			->get();

		foreach ( $records as $key => $record ) {
			$records[ $key ] = $this->prepare( $record );
		}

		return $records;
	}

	/**
	 * Count records in the database
	 *
	 * @param array $query
	 *
	 * @return int
	 */
	public function count( array $query = [] ): int {
		try {
			return wp_query_builder()
				->select( '*' )
				->from( 'celersearch_views' )
				->where( $query )
				->count();
		} catch ( \Exception $e ) {
			return 0;
		}
	}
}
