<?php

namespace CelerSearch\Repositories;

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

class IndexRepository implements IRepository {

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

	/**
	 * Get index by slug
	 *
	 * @param string $slug
	 *
	 * @return IndexConfig|null
	 */
	public function find_by_slug( string $slug ) : ?IndexConfig {
		global $wpdb;
		$table = $wpdb->prefix . 'celersearch_indices';
		// 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 WHERE slug = %s LIMIT 1", $slug ) );

		if ( ! $row ) {
			return null;
		}

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

	/**
	 * Store specific record in the database
	 *
	 * @param array $data
	 *
	 * @return IndexConfig|null
	 */
	public function create( array $data ) : ?IndexConfig {
		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['slug'] ) ) {
			$row['slug'] = $data['slug'];
			$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';
		}

		if ( isset( $data['service_id'] ) ) {
			$row['service_id'] = is_null( $data['service_id'] ) ? $data['service_id'] : (int) $data['service_id'];
			$format[]          = is_null( $data['service_id'] ) ? '%s' : '%d';
		}

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

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

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

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

		$row['created_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 IndexConfig|null
	 */
	public function update( int $id, array $data ) : ?IndexConfig {
		global $wpdb;
		$table_name = $this->get_table_name();
		$row        = [];
		$format     = [];
		if ( isset( $data['name'] ) ) {
			$row['name'] = $data['name'];
			$format[]    = '%s';
		}
		if ( isset( $data['slug'] ) ) {
			$row['slug'] = $data['slug'];
			$format[]    = '%s';
		}
		if ( isset( $data['type'] ) ) {
			$row['type'] = $data['type'];
			$format[]    = '%s';
		}
		if ( isset( $data['config'] ) ) {
			$row['config'] = wp_json_encode( $data['config'] );
			$format[]      = '%s';
		} else if ( array_key_exists( 'config', $data ) && is_null( $data['config'] ) ) {
			$row['config'] = null;
			$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['service_id'] ) ) {
			$row['service_id'] = (int) $data['service_id'];
			$format[]          = '%d';
		} else if ( array_key_exists( 'service_id', $data ) && is_null( $data['service_id'] ) ) {
			$row['service_id'] = null;
			$format[]          = '%s';
		}
		if ( isset( $data['total_remote'] ) ) {
			$row['total_remote'] = $data['total_remote'];
			$format[]            = '%d';
		} else if ( array_key_exists( 'total_remote', $data ) && is_null( $data['total_remote'] ) ) {
			$row['total_remote'] = null;
			$format[]            = '%s';
		}
		if ( isset( $data['total_local'] ) ) {
			$row['total_local'] = $data['total_local'];
			$format[]           = '%d';
		} else if ( array_key_exists( 'total_local', $data ) && is_null( $data['total_local'] ) ) {
			$row['total_local'] = 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['synced_at'] ) ) {
			$row['synced_at'] = $data['synced_at'];
			$format[]         = '%s';
		} else if ( array_key_exists( 'synced_at', $data ) && is_null( $data['synced_at'] ) ) {
			$row['synced_at'] = null;
			$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 IndexConfig
	 */
	public function prepare( object $row ) : IndexConfig {
		if ( isset( $row->config ) ) {
			$row->config = json_decode( $row->config );
		}

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

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

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

		return new IndexConfig( $row );
	}

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

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

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

	/**
	 * Update index settings only (merges with existing config)
	 *
	 * @param int   $id       Index ID
	 * @param array $settings Settings to update
	 *
	 * @return IndexConfig|null
	 */
	public function update_settings( int $id, array $settings ): ?IndexConfig {
		$index = $this->find( $id );
		if ( ! $index ) {
			return null;
		}

		// Get existing config
		$config = $index->getConfig();
		$config = is_object( $config ) ? json_decode( wp_json_encode( $config ), true ) : ( is_array( $config ) ? $config : [] );

		// Ensure settings is an array
		$existing_settings = isset( $config['settings'] ) && is_array( $config['settings'] ) ? $config['settings'] : [];

		// Merge settings into config
		$config['settings'] = array_merge( $existing_settings, $settings );

		// Update the record
		return $this->update( $id, [ 'config' => $config ] );
	}
}