<?php

namespace CelerSearch\Repositories;

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

class ServiceRepository implements IRepository {

	const STATUS_CONNECTED = 'connected';
	const STATUS_FAILING = 'failed';
	const STATUS_PENDING = 'pending';
	const FAILURE_THRESHOLD = 3;

	/**
	 * Retrieve specific record from the database
	 *
	 * @param int $id
	 *
	 * @return ServiceConfig|null
	 */
	public function find( int $id ): ?ServiceConfig {
		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 ( ! is_object( $row ) ) {
			return null;
		}

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

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

		$table_name = $this->get_table_name();

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

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

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

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

		if ( isset( $data['status'] ) ) {
			$row['status'] = $data['status'];
			$format[]      = '%s';
		} else if ( array_key_exists( 'status', $data ) && is_null( $data['status'] ) ) {
			$row['status'] = null;
			$format[]      = '%s';
		}

		if ( isset( $data['checked_at'] ) ) {
			$row['checked_at'] = $data['checked_at'];
			$format[]          = '%s';
		} else if ( array_key_exists( 'checked_at', $data ) && is_null( $data['checked_at'] ) ) {
			$row['checked_at'] = null;
			$format[]          = '%s';
		}


		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery -- Custom table
		$result = $wpdb->insert( $table_name, $row, $format );
		if ( (int) $result > 0 ) {
			return $this->find( $wpdb->insert_id );
		} else {
			return null;
		}
	}

	/**
	 * Update specific record in the database
	 *
	 * @param int $id
	 * @param array $data
	 *
	 * @return ServiceConfig|null
	 */
	public function update( int $id, array $data ) : ?ServiceConfig {
		global $wpdb;
		$table_name = $this->get_table_name();
		$row        = [];
		$format     = [];
		if ( isset( $data['name'] ) ) {
			$row['name'] = $data['name'];
			$format[]    = '%s';
		}
		if ( isset( $data['provider'] ) ) {
			$row['provider'] = $data['provider'];
			$format[]        = '%s';
		}
		if ( isset( $data['config'] ) ) {
			$row['config'] = wp_json_encode( $data['config'] );
			$format[]      = '%s';
		}
		if ( isset( $data['status'] ) ) {
			$row['status'] = $data['status'];
			$format[]      = '%s';
		} else if ( array_key_exists( 'status', $data ) && is_null( $data['status'] ) ) {
			$row['status'] = null;
			$format[]      = '%s';
		}
		if ( isset( $data['checked_at'] ) ) {
			$row['checked_at'] = $data['checked_at'];
			$format[]          = '%s';
		} else if ( array_key_exists( 'checked_at', $data ) && is_null( $data['checked_at'] ) ) {
			$row['checked_at'] = null;
			$format[]          = '%s';
		}
		if ( isset( $data['failure_count'] ) ) {
			$row['failure_count'] = (int) $data['failure_count'];
			$format[]             = '%d';
		}

		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Custom table
		if ( $wpdb->update( $table_name, $row, [ 'id' => $id ], $format, [ '%d' ] ) ) {
			return $this->find( $id );
		} else {
			return null;
		}
	}

	/**
	 * Delete specific record in the database
	 *
	 * @param int $id
	 *
	 * @return mixed
	 */
	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 ServiceConfig
	 */
	public function prepare( object $row ) : ServiceConfig {

		if ( ! empty( $row->config ) ) {
			$row->config = json_decode( $row->config );
		}

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

		return new ServiceConfig( $row );
	}

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

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

	/**
	 * Retrieve multiple records from the database
	 *
	 * @param array $query
	 * @param int $page
	 * @param int $per_page
	 * @param string $order
	 * @param string $orderBy
	 *
	 * @return ServiceConfig[]
	 * @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_services' )
			->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_services' )
				->where( $query )
				->count();
		} catch ( \Exception $e ) {
			return 0;
		}
	}
}