<?php namespace ProcessWire;

/**
 * ProcessWire InputfieldCommentsAdmin
 *
 * An Inputfield for handling administration of Comments.
 * 
 * ProcessWire 3.x, Copyright 2022 by Ryan Cramer
 * https://processwire.com
 *
 * @property bool|int $useManager
 * @method InputfieldMarkup renderItem(Comment $comment, $n)
 *
 */

class InputfieldCommentsAdmin extends Inputfield implements InputfieldItemList {

	public static function getModuleInfo() {
		return array(
			'title' => __('Comments Admin', __FILE__),
			'version' => 104,
			'summary' => __('Provides an administrative interface for working with comments', __FILE__),
			'permanent' => false, 
			'requires' => 'FieldtypeComments',
			);
	}
	
	protected $commentIDsToNumbers = array();
	
	protected $commentsManagerUrl = '';

	protected $statuses = array();
	
	public function __construct() {
		$this->set('useManager', false); 
		parent::__construct();
	}
	
	public function init() {
		parent::init();
		require_once(__DIR__ . '/FieldtypeComments.module');
		if($this->wire()->modules->isInstalled('ProcessCommentsManager')) {
			$this->commentsManagerUrl = $this->wire()->config->urls->admin . 'setup/comments/';
		}
		$this->statuses = array(
			Comment::statusFeatured => array($this->_x('Featured', 'comment-status'), 'trophy', 'featured'),
			Comment::statusApproved => array($this->_x('Approved', 'comment-status'), 'check-circle', 'approved'),
			Comment::statusPending => array($this->_x('Pending', 'comment-status'), 'commenting-o', 'pending'),
			Comment::statusSpam => array($this->_x('Spam', 'comment-status'), 'warning', 'spam'),
		);
	}
	
	/**
	 * @param Comment $comment
	 * @param int $n
	 * @return array|mixed|null|_Module|Field|Fieldtype|Module|NullPage|Page|Permission|Role|Template|WireData|WireInputData|string
	 * 
	 */
	protected function ___renderItem(Comment $comment, $n) {
		
		$name = $this->name;
		$names = array(
			'status' => "{$this->name}_status_{$comment->id}",
			'parent' => "{$this->name}_parent_id_{$comment->id}",
			'cite' => "{$name}_cite_{$comment->id}",
			'email' => "{$name}_email_{$comment->id}",
			'website' => "{$name}_website_{$comment->id}",
			'stars' => "{$name}_stars_{$comment->id}",
			'text' => "{$name}_text_{$comment->id}"
		);


		$sanitizer = $this->wire()->sanitizer;
		$statusName = '';
		$statusIcon = '';
		$statusOut = "<select id='$names[status]' name='$names[status]'>";
		
		foreach($this->statuses as $status => $statusInfo) {
			list($label, $icon, /*$name*/) = $statusInfo;
			if($comment->status == $status) {
				$selected = " selected='selected'";
				$statusName = $label;
				$statusIcon = $icon;
			} else {
				$selected = '';
			}
			$statusOut .= "<option value='$status'$selected>$label</option>";
		}
		
		$statusOut .= "<option value='delete{$comment->id}'>" . $this->_('Delete') . "</option>";
		$statusOut .= "</select>";
		
		$parentOut = "<select name='$names[parent]' id='$names[parent]'><option value='0'></option>";
		$parentID = $comment->parent_id; 
		$_n = 0;
		foreach($this->value as $_comment) {
			$_n++;
			if($_comment->id == $comment->id) continue; 
			if($_comment->created > $comment->created) continue; 
			$selected = $parentID == $_comment->id ? " selected='selected'" : "";
			$parentOut .= 
				"<option$selected value='$_comment->id'>" . 
				sprintf($this->_('Comment #%d (%s)'), $_n, $this->wire('sanitizer')->entities($_comment->cite)) . 
				"</option>";
		}
		$parentOut .= "</select>";
		
		$notifyOut = '';
		if($comment->getField()->useNotify) {
			if($comment->flags & Comment::flagNotifyAll) $notifyOut = $this->_('ALL new comments');
				else if($comment->flags & Comment::flagNotifyReply) $notifyOut = $this->_('REPLIES only'); 
				else $notifyOut = $this->_('OFF'); 
		}

		$headLabel = $statusName; 
		$num = $n+1; 

		$liID = "CommentsAdminItem{$comment->id}";
		/** @var InputfieldMarkup $f */
		$f = $this->wire('modules')->get('InputfieldMarkup');
		$f->attr('id', $liID);
		$f->addClass("CommentsAdminItem$statusName"); 
		$f->icon = $statusIcon;

		if($comment->status == Comment::statusSpam) {
			$f->addClass("CommentsAdminItemSpam InputfieldIsError InputfieldStateError", 'wrapClass');
			$f->description = $this->_("Spam is automatically deleted after the amount of time specified in the field configuration.");
			$f->collapsed = Inputfield::collapsedYes;
		} else if($comment->status == Comment::statusApproved) {
			$f->collapsed = Inputfield::collapsedYes;
			$f->addClass('InputfieldIsSuccess');
		} else if($comment->status == Comment::statusPending) {
			$f->addClass('InputfieldIsPrimary');
			$f->description = $this->_("This item is awaiting approval or deletion.");
		}
		
		$cite = $sanitizer->entities($comment->cite);
		$email = $sanitizer->entities($comment->email);
		$website = $sanitizer->entities($comment->website);
		$stars = $sanitizer->entities($comment->stars);
		$text = $sanitizer->entities(trim($comment->text));
		
		$f->label = strtoupper($headLabel) . ': ' . sprintf(
			$this->_('Comment #%1$d Posted %2$s by %3$s'), 
			$num, 
			wireRelativeTimeStr($comment->created), 
			$cite	
		);
		
		if($this->hasField && $this->hasField->get('useStars')) {
			$starsInput = 
				"<p class='CommentsAdminItemStars'>" .
					"<label for='$names[stars]'>" . 
						"<span class='detail'>" . $this->_('Stars') . "</span>" . 
					"</label>" .
					"<input type='number' min='0' max='5' id='$names[stars]' name='$names[stars]' value='$stars' />" .
				"</p>";
		} else {
			$starsInput = '';
		}
		
		$notes = array();
		if($notifyOut) {
			$notes[] = $this->_('Email notifications:') . " $notifyOut";
		}
		if($this->commentsManagerUrl) {
			$metaQty = count($comment->getMeta());
			$page = $comment->getPage();
			$field = $comment->getField();
			$metaUrl = $this->commentsManagerUrl . "meta/?comment_id=$comment->id&page_id=$page->id&field_id=$field->id";
			// $editUrl = $this->commentsManagerUrl . "list/$field->name/all/?id=$comment->id";
			$notes[] = 
				"<a href='$metaUrl' class='pw-modal' data-buttons='button'>" . 
				sprintf($this->_n('%d meta value', '%d meta values', $metaQty), $metaQty) . "</a>";
		}
	
		$f->value =
			"<div class='ui-helper-clearfix'>" . 
				"<p class='CommentsAdminItemStatus'>" . 
					"<label for='$names[status]'><span class='detail'>" . $this->_('Status') . "</span></label>" . 
					$statusOut . 
				"</p>" .
				"<p class='CommentsAdminItemParent'>" . 
					"<label for='$names[parent]'><span class='detail'>" . $this->_('Reply To') . "</span></label>"  . 
					$parentOut . 
				"</p>" .
			"</div><div class='ui-helper-clearfix'>" . 
				"<p class='CommentsAdminItemCite'>" . 
					"<label for='$names[cite]'><span class='detail'>" . $this->_('Cite') . "</span></label>" . 
					"<input type='text' id='$names[cite]' name='$names[cite]' value='$cite' />" . 
				"</p>" .
				"<p class='CommentsAdminItemEmail'>" . 
					"<label for='$names[email]'><span class='detail'>" . $this->_('E-Mail') . "</span></label>" . 
					"<input type='text' id='$names[email]' name='$names[email]' value='$email' />" . 
				"</p>" .
				"<p class='CommentsAdminItemWebsite'>" . 
					"<label for='$names[website]'><span class='detail'>" . $this->_('Website') . "</span></label>" . 
					"<input type='text' id='$names[website]' name='$names[website]' value='$website' />" . 
				"</p>" .
				$starsInput . 
			"</div>" . 
			"<p class='CommentsAdminItemText'>" . 
				"<label for='$names[text]'><span class='detail'>" . $this->_('Text') . "</span></label>" . 
				"<textarea id='$names[text]' name='$names[text]' rows='5'>$text</textarea>" . 
			"</p>" .
			(count($notes) ? "<p class='CommentsAdminItemNotify detail'>" . implode(' &nbsp; • &nbsp; ', $notes) . "</p>" : "") .
			"<input class='CommentsAdminItemSort' type='hidden' name='sort_{$this->name}_{$comment->id}' value='$n' />" . 
			"";

		return $f; 
	}

	public function ___render() {

		$value = $this->value; 
		
		if($this->useManager || !count($value)) {
			$out = $this->renderValue();
			
		} else {
			$n = 0;
			foreach($value as $comment) {
				$this->commentIDsToNumbers[$comment->id] = ++$n;
			}

			$fieldset = new InputfieldWrapper(); // wired
			$this->wire($fieldset);

			$n = 0;
			foreach($value as $comment) {
				$f = $this->renderItem($comment, $n++);
				$f->addClass($this->wrapClass);
				$fieldset->add($f);
			}

			$out = $fieldset->render();
		}

		return $out; 
	}
	
	public function ___renderValue() {

		$value = $this->attr('value');
		
		if(!count($value)) {
			return "<p>" . $this->_('There are currently no items to display.') . "</p>";
		}

		$items = array();
		$qtys = array();
		$total = 0;
		$field = null;
		$page = null;
		
		foreach($value as $comment) {
			/** @var Comment $comment */
			$total++;
			if(!$field) $field = $comment->getField();
			if(!$page) $page = $comment->getPage();
			$status = $comment->status;
			if(!isset($this->statuses[$status])) continue;
			if(!isset($qtys[$status])) $qtys[$status] = 0;
			$qtys[$status]++;
		}
		
		$url = $this->commentsManagerUrl . "list/$field->name/all/?pages_id=$page->id";
		$icon = wireIconMarkup('comments');
		$item = $icon . ' ' . sprintf($this->_n('%d comment', '%d comments', $total), $total);
	
		if($page->editable($field)) $item = "<a href='$url' target='_blank'>$item</a>";
		$items[] = $item;
		
		foreach($this->statuses as $status => $statusInfo) {
			if(empty($qtys[$status])) continue;
			$qty = $qtys[$status];
			list($label, $icon, /*$name*/) = $statusInfo;
			$icon = wireIconMarkup($icon);
			$items[] = "<span class='ui-priority-secondary'>$icon $qty $label</span>";
		}
		
		return "<p>" . implode(' &nbsp; &nbsp; ', $items) . "</p>";
	}

	public function ___processInput(WireInputData $input) {
		
		if($this->useManager) return $this;

		$n = 1; 	
		$names = array(
			'cite', 
			'email',
			'website',
			'status',
			'delete',
			'parent_id', 
			'text',
			'sort',
		);
		
		if($this->hasField && $this->hasField->get('useStars')) $names[] = 'stars';

		foreach($this->value as $comment) {
			/** @var Comment $comment */

			$data = array();
			foreach($names as $name) {
				$inputName = $this->name . "_" . $name . "_" . $comment->id; 
				$value = isset($input[$inputName]) ? $input[$inputName] : '';
				$data[$name] = $value; 
			}

			if($data['status'] && $data['status'] == "delete{$comment->id}") {
				$this->value->remove($comment); 
				$this->message(sprintf($this->_('Removed comment #%d'), $n)); 
				$this->value->trackChange('remove'); 
				continue;
			} 

			foreach($data as $key => $value) {
				if($key == 'text') $value = $comment->cleanCommentString($value); 
				if(($value || $key == 'status') && $comment->$key != $value) {
					$comment->$key = $value; 
					$this->message(sprintf($this->_('Updated %s for comment #%d'), $key, $n)); 
					$this->value->trackChange('update'); 
				}	
			}
			$n++; 
		}

		return $this; 
	}
	
	public function ___getConfigInputfields() {
		$modules = $this->wire()->modules;
		$inputfields = parent::___getConfigInputfields();
		
		/** @var InputfieldToggle $f */
		$f = $modules->get('InputfieldToggle'); 
		$f->attr('name', 'useManager'); 
		$f->label = $this->_('Use comments manager rather than in-page editor?'); 
		$f->description = 
			$this->_('When enabled user will be directed to the dedicated comments manager for editing comments.') . ' ' . 
			$this->_('This may be preferable because the comments manager has more features and uses pagination.'); 
		$value = (int) $this->get('useManager'); 
		$f->val($value);
		$f->themeOffset = 1;
		$inputfields->prepend($f);
		if($value && !$this->commentsManagerUrl) {
			$modules->getInstall('ProcessCommentsManager'); 
		}
		
		return $inputfields;
	}

}
