<?php

namespace GemBoards\Classes;

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

use Exception;
use GemBoards\Classes\Exception\GemboardsException;
use GemBoards\Classes\Exception\GemboardsInvalidArgumentException;
use GemBoards\Collections\NotifiedUserCollection;
use GemBoards\Entities\NotifiedUser;
use GemBoards\Utils\Helper;

class Notification {
	private ?int $notification_id = null;
	private ?int $board_id        = null;
	private ?int $task_id         = null;
	private ?int $object_id       = null;
	private string $object_type;
	private string $event_type;
	private ?string $message = null;
	private array $payload   = [];
	private array $to_users  = [];
	private ?int $from_user  = null;

	public function __construct(
		string $event_type,
		string $object_type,
		string $message
	) {
		$this->event_type  = $event_type;
		$this->object_type = $object_type;
		$this->message     = $message;
	}

	public function set_type(
		?string $event_type = null,
		?string $object_type = null,
		?string $message = null
	) {
		$this->event_type  = is_null( $event_type ) ? $this->event_type : $event_type;
		$this->object_type = is_null( $object_type ) ? $this->object_type : $object_type;
		$this->message     = is_null( $message ) ? $this->message : $message;
	}

	public function set_ref(
		?int $board_id = null,
		?int $task_id = null,
		?int $object_id = null
	): self {
		$this->board_id  = $board_id;
		$this->task_id   = $task_id;
		$this->object_id = $object_id;

		return $this;
	}

	public function set_payload( array $payload ): self {
		$this->payload = array_merge( $this->payload, $payload );

		return $this;
	}

	public function from_user( int $id ): self {
		$this->from_user = $id;

		return $this;
	}

	public function to_users( array $ids ): self {
		if ( empty( $ids ) ) {
			throw new Exception( esc_html__( 'To user id[s] is empty', 'gemboards' ) );
		}
		$this->to_users = $ids;

		return $this;
	}

	/**
	 * @throws Exception
	 */
	public function to_users_by_username( array $usernames ): self {
		if ( empty( $usernames ) ) {
			throw new Exception( esc_html__( 'To user id[s] is empty', 'gemboards' ) );
		}
		$this->to_users = get_users( [
			'login__in' => $usernames,
			'fields'    => 'ID',
		] );

		if ( empty( $this->to_users ) ) {
			throw new Exception( esc_html__( 'Invalid to user id[s]', 'gemboards' ) );
		}

		return $this;
	}

	public function send(): bool {
		$data = [
			'from_user_id' => $this->from_user,
			'object_id'    => $this->object_id,
			'object_type'  => $this->object_type,
			'event_type'   => $this->event_type,
			'message'      => $this->message,
			'meta'         => $this->payload,
		];

		$id           = $this->notification_id;
		$notification = new \GemBoards\Entities\Notification( $id ?? 0 );
		foreach ( $data as $key => $value ) {
			$method = 'set_' . $key;
			if ( ! method_exists( $notification, $method ) ) {
				continue;
			}
			$notification->$method( $value );
		}
		$result = $notification->save();
		if ( is_wp_error( $result ) ) {
			return false;
		}

		foreach ( $this->to_users as $to_user ) {
			$notified_user = new NotifiedUser();
			$notified_user->set_to_user_id( $to_user );
			$notified_user->set_notification_id( $id );
			$notified_user->save();
		}

		return true;
	}

	/**
	 * @throws Exception
	 */
	public static function by_id( int $id ): self {
		$notification = Helper::get_notification( $id );
		if ( ! $notification ) {
			throw new Exception( esc_html__( 'Invalid notification id', 'gemboards' ) );
		}

		$ins                  = new static( $notification->get_event_type(), $notification->get_object_type(), $notification->get_message() );
		$ins->notification_id = $id;
		$ins->set_ref( $notification->get_object_id(), null, $notification->get_object_id() );
		$ins->from_user( $notification->get_from_user_id() );
		$ins->set_payload( json_decode( $notification->get_notification_meta(), true ) );

		return $ins;
	}

	/**
	 * @throws GemboardsInvalidArgumentException
	 * @throws GemboardsException
	 */
	public function exclude_users( array $ids ): bool {
		if ( empty( $this->notification_id ) ) {
			throw new Exception( esc_html__( 'Invalid notification  id', 'gemboards' ) );
		}

		$notified_users = new NotifiedUserCollection( [
			'per_page' => - 1,
			'where'    => [
				[
					'key'   => 'notification_id',
					'value' => $this->notification_id,
				],
				[
					'key'     => 'to_user_id',
					'value'   => $ids,
					'compare' => 'IN',
				],
			],
		] );
		foreach ( $notified_users->get_results() as $notified_user ) {
			$notified_user->delete();
		}

		return true;
	}

	/**
	 * @throws GemboardsInvalidArgumentException
	 * @throws GemboardsException
	 * @throws Exception
	 */
	public function delete(): bool {
		if ( empty( $this->notification_id ) ) {
			throw new Exception( esc_html__( 'Invalid notification  id', 'gemboards' ) );
		}
		$notification = Helper::get_notification( $this->notification_id );
		if ( ! $notification ) {
			return false;
		}
		$notification->delete();

		$notified_users = new NotifiedUserCollection( [
			'per_page' => - 1,
			'where'    => [
				[
					'key'   => 'notification_id',
					'value' => $this->notification_id,
				],
			],
		] );
		foreach ( $notified_users->get_results() as $notified_user ) {
			$notified_user->delete();
		}

		return true;
	}
}
