<?php

namespace Limb_Chatbot\Includes\Repositories;

use Limb_Chatbot\Includes\Data_Objects\Widget;
use Limb_Chatbot\Includes\Services\Collection;
use Limb_Chatbot\Includes\Services\Helper;
use Limb_Chatbot\Includes\Services\User_Roles;

/**
 * Repository for widget persistence and querying.
 *
 * @since 2.0.0
 */
class Widget_Repository {

	/**
	 * Get widgets collection with filtering and pagination.
	 *
	 * @param array $params Query params.
	 * @return Collection
	 */
	public function get_items( array $params = [] ): Collection {
		$user_role = '';
		if ( array_key_exists( 'type', $params ) && is_string( $params['type'] ) && $params['type'] !== '' ) {
			$params['type'] = sanitize_key( $params['type'] );
		}

		if ( array_key_exists( 'published', $params ) && $params['published'] !== '' ) {
			$params['published'] = (int) (bool) $params['published'];
		}
		if ( array_key_exists( 'user_role', $params ) && is_string( $params['user_role'] ) ) {
			$user_role = sanitize_key( $params['user_role'] );
		}

		$order_by         = $params['orderby'] ?? 'order';
		$allowed_order_by = [ 'id', 'type', 'order', 'created_at', 'updated_at' ];
		if ( ! in_array( $order_by, $allowed_order_by, true ) ) {
			$order_by = 'order';
		}
		if ( $order_by === 'order' ) {
			$order_by = '`order`';
		}

		$order  = strtoupper( $params['order'] ?? 'ASC' );
		$offset = 1;
		$limit  = -1;
		unset( $params['page'], $params['per_page'], $params['user_role'] );

		$items = Widget::where( $params, $limit, $offset, $order_by, $order );

		if ( '' !== $user_role ) {
			$items = $this->filter_by_user_role( $items, $user_role );
		}

		return $items;
	}

	/**
	 * Filter widgets by user role in visibility.
	 *
	 * Keeps widgets that include `everyone` or the requested role key.
	 *
	 * @param  Collection  $widgets  Widgets collection.
	 * @param  string  $role_key  Role key to filter by.
	 *
	 * @return Collection
	 * @since 2.0.0
	 */
	private function filter_by_user_role( Collection $widgets, string $role_key ): Collection {
		if ( null === User_Roles::instance()->get_by_key( $role_key ) ) {
			return new Collection( [] );
		}

		return $widgets->filter( static function ( $widget ) use ( $role_key ) {
			$visibility = is_object( $widget ) && isset( $widget->visibility ) && is_array( $widget->visibility )
				? $widget->visibility
				: [];

			if ( ! array_key_exists( 'roles', $visibility ) || ! is_array( $visibility['roles'] ) ) {
				return false;
			}

			$roles = array_values(
				array_filter(
					array_map(
						static function ( $role ) {
							return is_string( $role ) ? sanitize_key( $role ) : '';
						},
						$visibility['roles']
					)
				)
			);

			return in_array( User_Roles::ROLE_EVERYONE, $roles, true ) || in_array( $role_key, $roles, true );
		} );
	}

	/**
	 * Fetch single widget by numeric id.
	 *
	 * @param int $id Widget id.
	 * @return Widget|null
	 */
	public function get_item( int $id ): ?Widget {
		return Widget::find( $id );
	}

	/**
	 * Fetch single widget by UUID.
	 *
	 * @param string $uuid Widget uuid.
	 * @return Widget|null
	 */
	public function get_item_by_uuid( string $uuid ): ?Widget {
		return Widget::where( [ 'uuid' => $uuid ] )->first() ?? null;
	}

	/**
	 * Create widget row.
	 *
	 * @param array $data Widget payload.
	 * @return Widget|null
	 * @throws \Exception
	 */
	public function create( array $data ): ?Widget {
		return Widget::create( $data );
	}

	/**
	 * Update widget row.
	 *
	 * @param int $id Widget id.
	 * @param array $data Update payload.
	 * @return Widget|null
	 */
	public function update( int $id, array $data ): ?Widget {
		return Widget::update( [ 'id' => $id ], $data );
	}

	/**
	 * Delete widget row.
	 *
	 * @param int $id Widget id.
	 * @return bool
	 */
	public function delete( int $id ): bool {
		return (bool) Widget::delete( [ 'id' => $id ] );
	}

	/**
	 * Delete multiple widgets.
	 *
	 * @param array $ids Widget IDs.
	 * @return bool
	 */
	public function batch_delete( array $ids ): bool {
		return (bool) Widget::delete( [ 'id' => $ids ] );
	}

	/**
	 * Batch create/update widgets.
	 *
	 * @param array $items Widget payload items.
	 * @return Collection
	 * @throws \Exception
	 */
	public function batch( array $items ): Collection {
		$collection = new Collection();
		foreach ( $items as $item ) {
			if ( ! is_array( $item ) ) {
				continue;
			}
			$widget = null;
			if ( ! empty( $item['id'] ) && ( $this->get_item( $item['id'] ) instanceof Widget ) ) {
				$widget = $this->update( (int) $item['id'], $item );
			} else {
				unset( $item['id'] );
				if ( empty( $item['uuid'] ) ) {
					$item['uuid'] = Helper::get_uuid();
				}
				$widget = $this->create( $item );
			}
			if ( $widget instanceof Widget ) {
				$collection->push_item( Widget::find( $widget->get_id() ) );
			}
		}

		return $collection;
	}
}
