<?php

namespace Limb_Chatbot\Includes\Data_Objects;

use Limb_Chatbot\Includes\Factories\Widget_Config_Factory;
use Limb_Chatbot\Includes\Interfaces\Conditional_Visibility_Interface;
use Limb_Chatbot\Includes\Services\User_Manager;
use Limb_Chatbot\Includes\Services\User_Roles;

/**
 * Widget data object persisted in lbaic_widgets table.
 *
 * @since 2.0.0
 */
class Widget extends WPDB_Data_Object {
	const TABLE_NAME = 'lbaic_widgets';

	const FILLABLE = [
		'id',
		'uuid',
		'type',
		'published',
		'config',
		'locations',
		'visibility',
		'order',
		'created_at',
		'updated_at',
	];

	public string $uuid = '';

	public string $type = '';

	public int $published = 1;

	public array $config = [];

	public array $locations = [];

	public array $visibility = [ 'roles' => [ 'everyone' ] ];

	public int $order = 0;

	public string $created_at = '';

	public string $updated_at = '';

	/**
	 * @json_excluded
	 * @var array
	 */
	public array $included = [];

	/**
	 * @json_excluded
	 * @var array|string[]
	 */
	public array $meta_properties = [ 'dependencies' ];

	public function __construct( $instance = null ) {
		if ( is_array( $instance ) ) {
			$instance = $this->decode_json_fields_from_array( $instance );
		} elseif ( is_object( $instance ) ) {
			$instance = $this->decode_json_fields_from_object( $instance );
		}
		parent::__construct( $instance );
	}

	private function decode_json_fields_from_array( array $instance ): array {
		foreach ( [ 'config', 'locations', 'visibility' ] as $field ) {
			if ( isset( $instance[ $field ] ) && is_string( $instance[ $field ] ) ) {
				$decoded = json_decode( $instance[ $field ], true );
				if ( json_last_error() === JSON_ERROR_NONE && is_array( $decoded ) ) {
					$instance[ $field ] = $decoded;
				}
			}
		}

		return $instance;
	}

	private function decode_json_fields_from_object( object $instance ): object {
		foreach ( [ 'config', 'locations', 'visibility' ] as $field ) {
			if ( isset( $instance->{$field} ) && is_string( $instance->{$field} ) ) {
				$decoded = json_decode( $instance->{$field}, true );
				if ( json_last_error() === JSON_ERROR_NONE && is_array( $decoded ) ) {
					$instance->{$field} = $decoded;
				}
			}
		}

		return $instance;
	}

	public static function find_by_uuid( string $value ) {
		return Widget::where( [ 'uuid' => $value ] )->first() ?? null;
	}

	public function get_published(): int {
		return $this->published;
	}

	public function set_published( int $published ): void {
		$this->published = $published ? 1 : 0;
	}

	public function get_locations(): array {
		return $this->locations;
	}

	public function set_locations( array $locations ): void {
		$this->locations = $locations;
	}

	public function get_visibility(): array {
		return $this->visibility;
	}

	public function set_visibility( array $visibility ): void {
		$this->visibility = $visibility;
	}

	public function get_order(): int {
		return $this->order;
	}

	public function set_order( int $order ): void {
		$this->order = $order;
	}

	public function is_for_new_chat(): bool {
		$locations = is_array( $this->locations ?? null ) ? $this->locations : [];
		foreach ( $locations as $location_group ) {
			if ( ! is_array( $location_group ) ) {
				continue;
			}
			foreach ( $location_group as $rule ) {
				if ( ! is_array( $rule ) ) {
					continue;
				}
				$param    = sanitize_key( $rule['param'] ?? '' );
				$operator = sanitize_text_field( $rule['operator'] ?? '' );
				$value    = sanitize_key( $rule['value'] ?? '' );

				if ( 'screen' === $param && '===' === $operator && 'new_chat' === $value ) {
					return true;
				}
			}
		}

		return false;
	}

	public function is_visible_for_current_user(): bool {
		$true       = false;
		$visibility = is_array( $this->visibility ?? null ) ? $this->visibility : [];
		if ( ! array_key_exists( 'roles', $visibility ) || ! is_array( $visibility['roles'] ) ) {
			return false;
		}

		$widget_roles = User_Roles::normalize_roles( $visibility['roles'] );
		if ( empty( $widget_roles ) ) {
			return false;
		}
		$chatbot_user = User_Manager::instance()->get_current_user();
		if ( in_array( User_Roles::ROLE_EVERYONE, $widget_roles, true ) ) {
			$true = true;
		} else {
			$current_user_roles = User_Roles::resolve_current_chatbot_user_roles( $chatbot_user );
			foreach ( $current_user_roles as $role ) {
				if ( in_array( $role, $widget_roles, true ) ) {
					$true = true;
					break;
				}
			}
		}
		if ( $true ) {
			$config = ( new Widget_Config_Factory() )->make( $this->get_type() );
			if ( $config instanceof Conditional_Visibility_Interface ) {
				return $config->should_appear( $this, $chatbot_user );
			}

			return true;
		} else {
			return false;
		}
	}

	public function get_type(): string {
		return $this->type;
	}

	public function set_type( string $type ): void {
		$this->type = $type;
	}

	public function get_uuid(): string {
		return $this->uuid;
	}

	public function set_uuid( string $uuid ): void {
		$this->uuid = $uuid;
	}

	public function dependencies() {
		$config_handler = ( new Widget_Config_Factory() )->make( $this->type );
		$config_handler->set_config( $this->get_config() );

		return $config_handler->get_dependencies();
	}

	public function get_config(): array {
		return $this->config;
	}

	public function set_config( array $config ): void {
		$this->config = $config;
	}
}
