<?php

namespace Limb_Chatbot\Includes\Repositories;

use Limb_Chatbot\Includes\Services\Channel_Events\Channel_Event;
use Limb_Chatbot\Includes\Services\Channel_Events\Channel_Event_Types;
use Limb_Chatbot\Includes\Services\Collection;
use wpdb;

/**
 * Repository for retrieving and deleting admin notification events.
 *
 * Admin notifications are stored as rows in `lbaic_channel_events`.
 * This repository scopes all queries by:
 * - recipient_wp_user_id (the currently logged-in admin)
 * - admin.* event types
 */
class Admin_Notifications_Repository {

	/**
	 * lbaic_channel_events table name (without prefix).
	 */
	private const TABLE_NAME = 'lbaic_channel_events';

	/**
	 * @var wpdb
	 */
	private wpdb $wpdb;

	public function __construct( ?wpdb $wpdb = null ) {
		$this->wpdb = $wpdb ?? $GLOBALS['wpdb'];
	}

	/**
	 * Fetch admin notification events for a single WP user.
	 *
	 * Supports pagination and ordering.
	 *
	 * @param  int    $wp_user_id
	 * @param  array  $params
	 *
	 * @return Collection<Channel_Event>
	 */
	public function get_events_for_admin( int $wp_user_id, array $params ): Collection {
		$page     = isset( $params['page'] ) ? max( 1, (int) $params['page'] ) : 1;
		$per_page = isset( $params['per_page'] ) ? max( 1, (int) $params['per_page'] ) : 20;
		$per_page = min( 100, $per_page );

		$order_by = isset( $params['orderby'] )
			? (string) $params['orderby']
			: ( isset( $params['order_by'] ) ? (string) $params['order_by'] : 'created_at' );
		$order_by = in_array( $order_by, [ 'created_at', 'id' ], true ) ? $order_by : 'created_at';

		$order = isset( $params['order'] ) ? (string) $params['order'] : 'desc';
		$order = in_array( strtolower( $order ), [ 'asc', 'desc' ], true ) ? strtoupper( $order ) : 'DESC';

		$since = null;
		if ( array_key_exists( 'since', $params ) && $params['since'] !== '' && $params['since'] !== null ) {
			$since_int = (int) $params['since'];
			$since     = $since_int > 0 ? $since_int : null;
		}

		$event_types = $this->get_admin_event_types();

		$table = $this->wpdb->prefix . self::TABLE_NAME;
		if ( ! $this->table_exists( $table ) ) {
			$collection = new Collection( [] );
			$collection->set_total( 0 );

			return $collection;
		}

		$type_placeholders = implode( ',', array_fill( 0, count( $event_types ), '%s' ) );

		// Total count (for pagination metadata).
		$where_since_sql = '';
		$where_since_arg = [];
		if ( $since !== null ) {
			$where_since_sql  = ' AND created_at > %s ';
			$where_since_arg[] = gmdate( 'Y-m-d H:i:s', $since );
		}

		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		$total = (int) $this->wpdb->get_var(
			$this->wpdb->prepare(
				"SELECT COUNT(*) FROM {$table}
				  WHERE recipient_wp_user_id = %d
				    AND event_type IN ({$type_placeholders})"
					. $where_since_sql,
				...array_merge( [ $wp_user_id ], $event_types, $where_since_arg )
			)
		);

		if ( $total <= 0 ) {
			$collection = new Collection( [] );
			$collection->set_total( 0 );

			return $collection;
		}

		// Fetch items.
		if ( $since !== null ) {
			// Continuous polling mode: no OFFSET, just cap results.
			// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
			$rows = $this->wpdb->get_results(
				$this->wpdb->prepare(
					"SELECT *
					 FROM {$table}
					 WHERE recipient_wp_user_id = %d
					   AND event_type IN ({$type_placeholders})
					   AND created_at > %s
					 ORDER BY {$order_by} {$order}, id {$order}
					 LIMIT %d",
					...array_merge( [ $wp_user_id ], $event_types, $where_since_arg, [ $per_page ] )
				),
				ARRAY_A
			);
		} else {
			$offset = ( $page - 1 ) * $per_page;
			// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
			$rows = $this->wpdb->get_results(
				$this->wpdb->prepare(
					"SELECT *
					 FROM {$table}
					 WHERE recipient_wp_user_id = %d
					   AND event_type IN ({$type_placeholders})
					 ORDER BY {$order_by} {$order}, id {$order}
					 LIMIT %d OFFSET %d",
					...array_merge( [ $wp_user_id ], $event_types, [ $per_page, $offset ] )
				),
				ARRAY_A
			);
		}

		$events = [];
		foreach ( (array) $rows as $row ) {
			$events[] = Channel_Event::from_row( $row );
		}

		$collection = new Collection( $events );
		$collection->set_total( $total );

		return $collection;
	}

	/**
	 * Delete a single admin notification event row.
	 *
	 * @param  int  $wp_user_id
	 * @param  int  $event_id
	 *
	 * @return bool
	 */
	public function delete_event_for_admin( int $wp_user_id, int $event_id ): bool {
		$table = $this->wpdb->prefix . self::TABLE_NAME;
		if ( ! $this->table_exists( $table ) ) {
			return false;
		}

		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		$deleted = $this->wpdb->delete(
			$table,
			[
				'id' => $event_id,
				'recipient_wp_user_id' => $wp_user_id,
			],
			[ '%d', '%d' ]
		);

		return ( false !== $deleted && (int) $deleted > 0 );
	}

	/**
	 * Mark one or more admin notification events as read.
	 *
	 * Only affects admin notification event types for the provided admin.
	 *
	 * @param  int    $wp_user_id
	 * @param  array<int,int>|string  $event_ids Array of event IDs, or "all".
	 *
	 * @return int Number of rows updated.
	 */
	public function mark_events_as_read_for_admin( int $wp_user_id, $event_ids ): int {
		$table = $this->wpdb->prefix . self::TABLE_NAME;
		if ( ! $this->table_exists( $table ) ) {
			return 0;
		}

		$mark_all = is_string( $event_ids ) && strtolower( trim( $event_ids ) ) === 'all';

		if ( ! $mark_all ) {
			$event_ids = is_array( $event_ids ) ? $event_ids : [];
			$event_ids = array_values(
				array_unique(
					array_filter(
						array_map( 'absint', $event_ids ),
						static function ( int $id ): bool {
							return $id > 0;
						}
					)
				)
			);
			if ( empty( $event_ids ) ) {
				return 0;
			}
		}

		$event_types = $this->get_admin_event_types();

		$type_placeholders = implode( ',', array_fill( 0, count( $event_types ), '%s' ) );

		if ( $mark_all ) {
			$sql = $this->wpdb->prepare(
				"UPDATE {$table}
				 SET is_read = 1
				 WHERE recipient_wp_user_id = %d
				   AND event_type IN ({$type_placeholders})",
				...array_merge( [ $wp_user_id ], $event_types )
			);
		} else {
			$id_placeholders = implode( ',', array_fill( 0, count( $event_ids ), '%d' ) );
			$sql             = $this->wpdb->prepare(
				"UPDATE {$table}
				 SET is_read = 1
				 WHERE recipient_wp_user_id = %d
				   AND event_type IN ({$type_placeholders})
				   AND id IN ({$id_placeholders})",
				...array_merge( [ $wp_user_id ], $event_types, $event_ids )
			);
		}

		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		$updated = $this->wpdb->query( $sql );

		return false === $updated ? 0 : (int) $updated;
	}

	/**
	 * Get unread admin notification count for a recipient.
	 *
	 * @param  int  $wp_user_id
	 *
	 * @return int
	 */
	public function get_unread_total_for_admin( int $wp_user_id ): int {
		$table = $this->wpdb->prefix . self::TABLE_NAME;
		if ( ! $this->table_exists( $table ) ) {
			return 0;
		}

		$event_types       = $this->get_admin_event_types();
		$type_placeholders = implode( ',', array_fill( 0, count( $event_types ), '%s' ) );

		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		$count = $this->wpdb->get_var(
			$this->wpdb->prepare(
				"SELECT COUNT(*) FROM {$table}
				 WHERE recipient_wp_user_id = %d
				   AND event_type IN ({$type_placeholders})
				   AND is_read = 0",
				...array_merge( [ $wp_user_id ], $event_types )
			)
		);

		return (int) $count;
	}

	/**
	 * Supported admin notification event types.
	 *
	 * @return array<int,string>
	 */
	private function get_admin_event_types(): array {
		return [
			Channel_Event_Types::ADMIN_NEW_CHAT,
			Channel_Event_Types::ADMIN_NEW_LEAD,
			Channel_Event_Types::ADMIN_NEW_CHATBOT_USER,
			Channel_Event_Types::ADMIN_NEW_FORM_ENTRY,
		];
	}

	/**
	 * Check whether the backing table exists.
	 *
	 * @param  string  $table
	 *
	 * @return bool
	 */
	private function table_exists( string $table ): bool {
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		return ( $this->wpdb->get_var( $this->wpdb->prepare( 'SHOW TABLES LIKE %s', $table ) ) === $table );
	}
}

